2 个回答
对外内存是为了进一步优化内存的使用以及提高Shuffle时排序的效率,Spark引入了堆外(Off-heap)内存,使之可以直接在工作节点的系统内存中开辟空间,存储经过序列化的二进制数据。堆外内存意味着把内存对象分配在Java虚拟机的堆以外的内存,这些内存直接受操作系统管理(而不是虚拟机)。这样做的结果就是能保持一个较小的堆,以减少垃圾收集对应用的影响。
利用 JDK Unsafe API(从 Spark 2.0开始,在管理堆外的存储内存时不再基于Tachyon,而是与堆外的执行内存一样, 基于JDK Unsafe API实现),Spark可以直接操作系统堆外内存,减少了不必要的内存开销,以及频繁的GC扫描和回收,提升了处理性能。堆外内存可以精确的申请和释放(堆外内存之所以能够被精确的申请和释放,是由于内存的申请和释放不再通过 JVM 机制,而是直接向操作系统申请,JVM对于内存的清理是无法准确指定时间点的,因此无法实现精确的释放),而且序列化的数据占用空间可以被精确计算,所以相比与堆内内存来说降低了管理的难度,也降低了误差。
在默认情况下堆外内存并不启用,可以通过配置spark.memory.offHeap.enabled参数启用,并由spark.memory.offHeap.size参数设定堆外空间的大小。除了没有other空间,堆外内存和堆内内存的划分方式相同,所有运行中的并发任务共享存储内存和执行内存。
利用 JDK Unsafe API(从 Spark 2.0开始,在管理堆外的存储内存时不再基于Tachyon,而是与堆外的执行内存一样, 基于JDK Unsafe API实现),Spark可以直接操作系统堆外内存,减少了不必要的内存开销,以及频繁的GC扫描和回收,提升了处理性能。堆外内存可以精确的申请和释放(堆外内存之所以能够被精确的申请和释放,是由于内存的申请和释放不再通过 JVM 机制,而是直接向操作系统申请,JVM对于内存的清理是无法准确指定时间点的,因此无法实现精确的释放),而且序列化的数据占用空间可以被精确计算,所以相比与堆内内存来说降低了管理的难度,也降低了误差。
在默认情况下堆外内存并不启用,可以通过配置spark.memory.offHeap.enabled参数启用,并由spark.memory.offHeap.size参数设定堆外空间的大小。除了没有other空间,堆外内存和堆内内存的划分方式相同,所有运行中的并发任务共享存储内存和执行内存。
发布于:3个月前 (01-22) IP属地:四川省
堆内内存的大小,由Spark应用程序启动时spark.executor.memory参数配置。Executor内存的并发任务共享JVM堆内内存,这些任务在缓存RDD数据和广播(Broadcast)数据时占用的内存被规划为存储(Storage)内存,而这些任务在执行Shuffle时占用的内存被规划为执行(Execution)内存,剩余的部分不做特殊规划,那些Spark内部的对象实例,或者用户定义的Spark应用程序中的对象实例,均占用剩余的空间,不同的管理模式下,这三部分占用的空间大小各不同。
Spark对堆内存的管理是一种逻辑上的规划式的管理,因为对象实例占用内存的申请和释放都是由JVM完成的,Spark只能在申请和释放前记录这些内存。
JVM的对象可以以序列化的方式存储,序列化的过程是将对象转换为二进制字节流,本质上可以理解为将非连续空间的链式存储转化为连续空间或块存储,在访问时则需要进行反序列化。对于Spark中序列化的对象是字节流形式的,其占用的内存大小可直接计算,而对于非序列化的对象,其占用的内存是通过周期性的采样近似估算而得。被Spark标记为释放的对象实例,很有可能在实际上并没有被JVM回收。导致实际可用的内存小于Spark记录的可用内存,从而无法完全避免内存溢出(OOM)的异常。
Spark对堆内存的管理是一种逻辑上的规划式的管理,因为对象实例占用内存的申请和释放都是由JVM完成的,Spark只能在申请和释放前记录这些内存。
JVM的对象可以以序列化的方式存储,序列化的过程是将对象转换为二进制字节流,本质上可以理解为将非连续空间的链式存储转化为连续空间或块存储,在访问时则需要进行反序列化。对于Spark中序列化的对象是字节流形式的,其占用的内存大小可直接计算,而对于非序列化的对象,其占用的内存是通过周期性的采样近似估算而得。被Spark标记为释放的对象实例,很有可能在实际上并没有被JVM回收。导致实际可用的内存小于Spark记录的可用内存,从而无法完全避免内存溢出(OOM)的异常。
发布于:3个月前 (01-22) IP属地:四川省
我来回答
您需要 登录 后回答此问题!