在 Orcale 的官方网站中,对于 G1 GC 的介绍可以参见 Garbage-First Garbage Collector 及 Garbage-First Garbage Collector Tuning,其中关键点如下:
G1 收集器是一个服务端的垃圾收集器,适用于具有大内存的多处理器机器。它极有可能满足垃圾回收(GC)暂停时间目标,同时实现高吞吐量。整堆操作(例如全局标记)与应用程序线程同时执行。这样可以防止与堆或活动数据大小成比例的中断。
在 Orcale 的官方网站中,对于 G1 GC 的介绍可以参见 Garbage-First Garbage Collector 及 Garbage-First Garbage Collector Tuning,其中关键点如下:
G1 收集器是一个服务端的垃圾收集器,适用于具有大内存的多处理器机器。它极有可能满足垃圾回收(GC)暂停时间目标,同时实现高吞吐量。整堆操作(例如全局标记)与应用程序线程同时执行。这样可以防止与堆或活动数据大小成比例的中断。
以下基于 JDK 8 中的 HashMap
进行分析,先看看这几个构造函数:
1 | /** |
可见,对于最常见的 new HashMap()
方法,仅仅将 loadFactor
设置为了默认的负载因子:0.75
,此时未对底层的数组 Node<K,V>[] table
进行初始化。
默认的负载因子为何要选择 0.75
呢?其中 HashMap 的 JavaDoc 中专门提到:
As a general rule, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the lookup cost (reflected in most of the operations of the HashMap class, including get and put). The expected number of entries in the map and its load factor should be taken into account when setting its initial capacity, so as to minimize the number of rehash operations. If the initial capacity is greater than the maximum number of entries divided by the load factor, no rehash operations will ever occur.
If many mappings are to be stored in a HashMap instance, creating it with a sufficiently large capacity will allow the mappings to be stored more efficiently than letting it perform automatic rehashing as needed to grow the table. Note that using many keys with the same hashCode() is a sure way to slow down performance of any hash table. To ameliorate impact, when keys are Comparable, this class may use comparison order among keys to help break ties.
我们看看 HashMap(int initialCapacity, float loadFactor)
方法中最后一行:this.threshold = tableSizeFor(initialCapacity)
,其中 tableSizeFor
方法实现如下:
1 | /** |
Coalesce Hints for SQL Queries,该特性用于控制输出的文件数,之前数仓同步时耗时较长,经过定位后发现大部分时间消耗在与 OSS 的数据交互上,主要是小文件引起,每张表的同步任务经过 shuffle 后默认会生成 200 个文件,后面优化为根据每张表的表记录数计算出一个合适的分区数使用上述 Hint 嵌入在 SQL 中,整个数仓同步耗时降低近 50%。
同时发现的问题还有 EMR-OSS 连接器中对 System.gc()
的显式调用,该 问题 会导致花费大量时间在不必要的 FullGC 上,后面移除了该调用以提升数仓同步速度。
Spark Partitioning & Partition Understanding
Spark SQL Shuffle Partitions
关于 NoClassDefFoundError,稍有经验的开发应该都遇到过,比如下面这个异常:
1 | java.lang.NoClassDefFoundError: org/apache/commons/lang/exception/NestableRuntimeException |
ClassNotFoundException 是引起 NoClassDefFoundError 的最常见原因,常见于实际需要使用的依赖版本与应用依赖不一致,这个问题非常常见,但是估计大家都没注意 ClassNotFoundException 为什么会被转换为 NoClassDefFoundError,在一次线上问题的排查过程中我查询了 JLS 中的类的详细初始化顺序 Detailed Initialization Procedure, 在此摘抄一段最核心的部分:
在 Spark、Flink 应用等场景下,经常会将业务代码构造为一个 Uber jar 提交至集群运行,在一次 Hive-UDF 的集成过程中,使用 Apache Maven Shade Plugin 进行 Uber jar 构建后,加载类时提示找不到类,我将该 Uber jar 拉取至本地解压后发现类是存在的,但错误日志仅提示找不到类,后经过反复排查,原来是 Jar 包签名问题导致,只是我遇到的场景并未提示签名问题导致难以排查。
1 | <filters> |
JAR File Specification, Signed JAR File
“Invalid signature file” when attempting to run a .jar
Appendix: Template for building a Jar with Dependencies | Apache Flink