标签:str idv lte net 比较 方法区 循环引用 xib collect
JVM 通过 垃圾收集-GC 自动管理内存堆中对象内存的分配和回收。JVM 通常采用分代垃圾收集器,以便于整理内存碎片。分代垃圾收集器就是基于对象不同生命周期,将堆分成不同的内存区域,然后组合使用不同的垃圾收集算法,可简单认为分为两部分组成:
内存回收主要考虑两个问题:
如何判断对象可被回收,判断策略:
采用何种方式进行回收,垃圾收集算法:
对象实例首先在 Eden 区分配,为了快速分配,Hotspot JVM 采用的是一种 bump-the-pointer 的线性分配方法。分配时一般都有大块连续内存可用,此方法就是检查剩余内存是否足够,给对象分配内存,然后更新指针偏移量和初始化对象。
线性分配效率固然高,但对多线程程序来说,分配内存的操作必须是线程安全的。可以使用全局锁但会影响性能,Hotspot JVM 采用的是一种 Thread-Local Allocation Buffers (TLABs) 的方法,为每个线程分配一个缓冲区,当TLAB满了,再加锁去申请,在线程内部就能使用bump-the-pointer,进而提高分配的吞吐量。
分配内存时,一些大对象有可能直接在老年代分配,在年轻代经过几轮Minor GC存活,达到一定年龄的对象,会被提升复制到老年代。
Hotspot JVM 自JDK 6u23开始支持逃逸分析,采用 Tracing GC 追踪堆中存活对象,显然,GC roots就是堆外的对象引用:
需要注意GC roots是一组对象引用而不是引用对象。
Hotspot JVM 提供了多个垃圾收集器让分代收集器组合使用:
串行收集器,单个GC线程执行所有垃圾收集工作
并行收集器,吞吐量收集器,多个GC线程加速垃圾回收
并发收集器,看重响应时间而不是吞吐量
常用JVM参数指定GC组合:
使用命令java -XX:+PrintFlagsFinal -version
或者jmap -heap <pid>
可以查看默认配置。
JVM堆最小为物理内存的 1/64,64位至少2G,即最小32M;最大为物理内存的 1/4,如128G内存,默认JVM最大32G,可以使用-Xms
和-Xmx
指定初始和最大大小。年轻代默认比例为NewRatio=2
,即占总堆的 1/3,可使用-Xmn
指定大小,Survivor空间比例为SurvivorRatio=8
,即每个Survivor占Eden的 1/8,因为有两个,所以占年轻代的 1/10。
JDK 8 默认垃圾收集器是ParallelOldGC,其中Parallel Scavenge默认打开AdaptiveSizePolicy,自适应调整各种参数,默认的SurvivorRatio 配置需要手动指定才能生效。比如指定堆大小为270M,那么各区域大小如下:
图 1 并行垃圾收集,指定SurvivorRatio=8,堆空间大小
JVM 中方法区的实现,不同版本之间存储内容存在差别:
永久代逻辑上是堆的组成部分,64位JVM默认大小为82M,但最大大小难以估计,因为程序里面有多少类,有多少方法以及常量池大小都很难估算,此外永久代垃圾收集与老年代绑定,任一区域满了都会触发Full GC,所以对永久代的调优很困难。
在JDK 8中,移除永久代,类元数据分配在本地内存中,默认情况下,可用内存不受限制,可使用MaxMetaspaceSize
设置可用的内存上限。Hotspot JVM 显式管理这部分内存,从OS申请空间,然后分成块,一个块绑定到一个特定的类加载器,类元数据就在这些块中分配,当类卸载或加载器标记被回收,这些块被释放重用或返回OS。
JDK 8中 GC cause有:年轻代内存分配失败,引起的Minor GC;年轻代提升到老年代,而老年代空间不足,引起的Full GC;元数据空间达到阈值,引起的GC。JDK 7之前,由于永久代空间不足引起的Full GC。不管是永久代或者元数据空间,也都会存在内存泄漏,比如web应用,当应用程序被卸载,那么它的war包中的所有类都是垃圾,如果不移除就会有内存泄漏。
在分析内存回收时,一定要谨记 GC roots 是一组对象引用或者说指针,拿废弃常量来说,完全可以通过GC root来识别,进而回收。
标签:str idv lte net 比较 方法区 循环引用 xib collect
原文地址:http://www.cnblogs.com/cxcoder/p/6536181.html