Java GC机制

来源:互联网 发布:身份证号码验证 java 编辑:程序博客网 时间:2024/06/01 09:59

前计

JAVA相对于C++的一大好处是不用程序员自己进行new/delete 来手动管理内存了。 虽然是好处,但这些年来,一直被人诟病,内存泄漏情况比较严重。打个比方,你有个自称很牛逼的管家说可以帮你搞定一切投资,刚开始你觉得很开心,自己不用做事情了。后来发现这管家总体来说还可以,但也存在吹牛逼的现象,有的时候他把你的资产漏掉一部分,这个时候估计你整个人都不爽了。

笔者开发的项目中曾经过一个用纯JAVA的写的的NOSql数据库Cassandra, 就是如此,作为数据库不能长久运行,不然就会内存泄漏到outofMemory, 然后挂掉,然后造成服务不可用。所以后来,我们利用该数据库的特性,为了保证由于内存泄漏造成的数据库服务宕机,我们写了个脚本,在每天夜里在保证整个服务不宕机的前提下,主动一个个重启这个数据库的节点。所以建议大家如果有条件的话,还是不要一上来就死扣Java GC回收,找泄漏点,可以尝试直接暴力重启,这样简单有效。(当然是在不影响服务的前提上。)


GC Roots

判定GC垃圾的方法是通过遍历GC ROOT ,然后查看对象是否被GC ROOT 链上引用。如果没有被GC ROOT 链上对象引用,那么即使有其他对象引用,也会被判断为垃圾。
GC Root 为:
类 --通过系统类加载器加载的类
线程 --活着的线程
栈 -- Java方法中的局部变量和参数
本地JNI -- Jni方法中的局部变量和参数
全局JNI -- 全局JNI的引用
监视器 -- 用于线程同步的监视器
一些被JVM自身引用的对象

分代回收

Java把内存分为年轻代和年老代,并实施分代回收策略。年老代存放存活时间比较长的区域。永久代存放一些类信息。
原因是:
1. 如果不分代,GC需要遍历内存中所有对象,做标记和压缩,这个效率会非常低。
2. Java的大多数对象是活的比较短的,生命期比较短暂。故前述的算法会比较低效。


年轻代回收

划代回收后,年轻代分为Eden区,Survivor 0, Survivor 1区。Survivor 区只有一个在使用。

为了加快内存分配,JVM使用了两种技术:
Bump-the-pointer:JVM跟踪在伊甸园空间创建的最后一个对象。这个对象会被放在伊甸园空间的顶部。如果之后再需要创建对象,只需要检查伊甸园空间是否有足够的剩余空间。如果有足够的空间,对象就会被创建在伊甸园空间,并且被放置在顶部。这样一来,每次创建新的对象时,只需要检查最后被创建的对象。这将极大地加快内存分配速度。
TLABs: 如果我们在多线程的情况下,事情将截然不同。如果想要以线程安全的方式以多线程在伊甸园空间存储对象,不可避免的需要加锁,而这将极大地的影响性能。TLABs 是HotSpot虚拟机针对这一问题的解决方案。该方案为每一个线程在伊甸园空间分配一块独享的空间,这样每个线程只访问他们自己的TLAB空间,再与bump-the-pointer技术结合可以在不加锁的情况下分配内存。

年轻代回收可分为如下几个步骤。
1. 内存分配在Eden区
2. 当Eden区域满了以后,触发minor GC,把存活的对象全部复制到Survivor 0区,然后清空Eden区
3. 当Eden区域再次满了以后,把在Survivor 0区和Eden区的存活对象,全部复制到Survivor 1区。然后清空Eden和Survivor 0, 同时Survivor 0作为备选区。每一次复制到Survivor区,对象年龄加一
4. 当Eden区域再次满了以后,重复3的动作,只不过备选区发生切换。3和4如此反复。
5. 在处理步骤3和4的时候,存活时间长的会被promte到年老区。如果是大对象,会被直接放到年老区。

年老代Mark-Sweep-Compact方式







垃圾回收算法


Serial GC

Serial GC 算法在垃圾回收的时候必须停止其他用户进程。老年代垃圾回收使用Mark-Sweep-Compact 标记整理算法。常常用于客户端那些实时性不是那么强的应用程序。比如打印文件,可以全部停止打印。
使用参数: -XX:+UseSerialGC

Parallel GC

Serial GC的并发版。使用多线程进行年取代回收。
使用参数: -XX:+UseParallelGC

Parallel GC Old

Parallel 年老版本。使用Mark-Summary-Compact算法
使用参数:-XX:+UseParallelGCOld

CMS 算法

使用Mark-Sweep 老年代算法。
总共会进行三次Mark,初次Mark为遍历类加载器附近的类,第二次Mark为并发Mark, Mark第一次Mark对象所引用关联的对象,Remark为标记在并发Mark时产生的新对象。
使用参数:
-XX:UseConcMarkSweepGC
-XX:ParallelCMSThreads=<n>


G1 算法

据称最前沿的回收算法,充分利用CPU,内存,并考虑并发。




0 0