天天看点

Java内存管理之垃圾回收机制

Java 目录:https://blog.csdn.net/dkbnull/article/details/87932486

        Java的垃圾回收机制可以监控每个Java对象,当某个对象处于不可达状态时,就回收该对象所占用的内存;同时,垃圾回收机制还对内存进行监控,负责清理内存分配、回收中产生的内存碎片。

        对于Java程序来说,垃圾回收机制完成的这两项工作工作量都很大,因此垃圾回收机制成为限制Java程序运行效率的重要因素。因此高效的垃圾回收机制就显得至关重要。高效的垃圾回收机制既能够保证垃圾回收的快速运行,又不能导致Java程序出现卡顿。

        前面说垃圾回收机制监控每个Java对象,但他不可能实时监控每个Java对象的状态(这将非常耗性能),因此当一个对象失去引用后,他并不是被立即回收,而是等到垃圾回收机制运行的时候才进行回收。

        对于一个垃圾回收器的设计算法来说,主要有串行回收、并行回收;并发执行、应用程序停止执行;压缩、不压缩、复制的设计。

        串行回收:不管系统有多少个CPU,始终只用一个CPU来执行垃圾回收操作。

        并行回收:多个CPU并行执行垃圾回收操作。

        比较:并行回收执行效率高,但复杂度增加,会导致内存碎片增加等问题。

        并发执行:垃圾回收与应用程序同时运行。

        应用程序终止:在执行垃圾回收时应用程序会暂停。

        比较:并发执行的垃圾回收系统开销大,需要更多的堆内存,和应用程序之间存在执行冲突问题(比如应用程序在垃圾回收的时候修改对象)。

        压缩:把所有处于可达状态的对象移动到一起,然后将剩余的内存全部清理。

        不压缩:只回收内存。

        复制:将所有处于可达状态的对象复制到另一块相同的内存中,对原内存进行全部清理。

        比较:压缩式垃圾回收可以减少内存碎片;不压缩式会有较多内存碎片,与压缩式相比回收内存快,但分配内存慢;复制式不会产生内存碎片,但复制时消耗时间并且需要额外的内存。

        现行的垃圾回收机制采用分代的方式来进行垃圾回收,针对不同的分代采用不同的回收设计。

        根据对象生存时间的长短,把堆内存分为3个代:Young代,Old代,Permanent代。

        垃圾回收机制根据不同的分代的特点采用不同的回收算法。

        Young代

        采用复制式垃圾回收。

        Young代中处于可达状态的对象数量比较少,可复制成本不大,可以发挥复制算法的优势。

        Young代由一个Eden区和两个Survivor区构成,大部分对象创建时先分配到Eden区中,一些大的对象可以直接进入Old区,Survivor区的对象为在Young代中经历过至少一次垃圾回收,这些对象在进入Old代之前先保留在Survivor区。同一时间,两个Survivor区一个用来保存对象,另一个为空,用来在垃圾回收时保存Young代中的对象。

        垃圾回收时,Eden区和第一个Survivor区的可达状态的对象都复制到第二个Survivor区,然后清空Eden区和第一个Survivor区。此时第一个Survivor区变成空闲区,下一次垃圾回收时将Eden区和第二个Survivor区的可达状态的对象复制到该区。

        Old代

        如果Young代中的对象经过多次垃圾回收后依然没有被回收掉,这个对象会被移动到Old代。

        Old代中的对象大部分都是从Young代中转移过来的,经过了多次垃圾回收,因此这些对象不会轻易变成不可达状态。所以Old代执行垃圾回收的频率相对较低。

        随着Young代中的对象不断转移到Old代,Old代中存储了较多的对象,因此Old代的存储空间一般比Young代大,而且Old代每次执行垃圾回收的时间也比较长。

        综上考虑,Old代使用压缩式垃圾回收,这样可以避免Old代大量复制对象,同时由于Old代一般不会有对象编程不可达状态,因此回收过程中不会产生大量碎片。

        Permanent代

        Permanent代主要用于装载Class、方法等信息,垃圾回收机制通常不会回收Permanent代中的对象。