垃圾回收机制主要完成以下两件事情:
1. 跟踪并监控每个java对象,当某个对象处理不可达状态时,回收该对象所占用的内存;
2. 清理分配、回收过程中所产生的内存碎片。
对于一个垃圾回收器的设计算法来说,大致有如下可供选择的设计:
1. 串行回收(Serial)和并行回收(Parallel):串行回收就是算管系统有多少个CPU,始终只用一个CPU来执行垃圾回收操作;并行回收就是把整个回收工作拆分成多个部分,每个部分由一个CPU负责,从而让多个CPU并行回收。并行回收的效率很高,但复杂度会增加,内存碎片也会增加;
2. 并发执行(Concurrent)和应用程序停止(Stop-the-world):应用程序停止的垃圾回收,会同时暂停应用;并发执行的垃圾回收不会暂停应用,但需要解决和应用程序的执行冲突,执行时需要更多的堆内存;
3. 压缩、不压缩和复制:复制——将堆内分成两个空间,从根访问每一个关联的可达对象,将空间A的所有可达对象复制到空间B,然后回收空间A;标记清除——也就是不压缩的回收方式,垃圾回收器先从根访问一次所有可达对象,将它们标记为可达状态,然后再遍历一次整个内存区域,把没有标记为可达对象的内存回收;标记压缩——即压缩的回收方式,可以说是复制回收和标记清除两种方式的综合,垃圾回收器先遍历一次内存区域,把所有可达对象标记为可达状态,在标记的过程中,把所有可达状态的对象放置到内存区域的同一块内存中,最后,把未标记的对象回收。
堆内存的分代回收——根据对象的生存时间长短,将对象分为不同的“代”,对不同代的对象采用不同的垃圾回收机制。垃圾回收器通常把对象分为三代,即Young、Old和Permanent,分代回收策略基于以下事实:
1. 绝大多数对象不会被长时间引用,这些对象在其Young期间就会被回收;
2. 很老的对象和很新的对象之间很少存在相互引用的关系。
Young代的回收通常采用复制算法。Young代由一个Eden区和两个Survivor区构成,绝大多数对象先分配到Eden区中(有一些大的对象可能会被直接分配到Old代中),Survivor区中的对象都至少在Young代中经历过一次垃圾回收,所以这些对象在被转移到Old代之前会先保留在Survivor区中。同一时间两个Survivor区中一个用来保存对象,而另一个是空的,用来在下次垃圾回收时保存Young代中的对象。每次复制就是将Eden和第一个Survivor中的可达对象复制到第二个Survivor区中,然后清空Eden和第一个Survivor区。
Old代的垃圾回收具有如下两个特征:
1. Old代垃圾回收的执行频率无需太高,因为很少有对象会死掉;
2. 每次对Old代执行垃圾回收需要更长的时间来完成。
基于以上考虑,垃圾回收器通常会采用标记压缩算法,也就是压缩式的垃圾回收算法来回收Old代的内存。
Permanent代主要用于装载Class、方法待信息,默认为64M,垃圾回收器通常不会回收Permanent代的对象。
Young和Old代的内存回收又分别被称为次要回收和主要回收。
部分与垃圾回收相关的附加选项:
1. -Xmx:设置Java虚拟机堆内存的最大容量,如java -Xmx256m xxx.class;
2. -Xms:设置Java虚拟机堆内存的初始容量,如java -Xms128m xxx.class;
3. -XX:MinHeapFreeRatio = 40,设置Java虚拟机堆内存最小的空闲百分比,默认值为40,如java -XX:MinHeapFreeRatio = 40 xxx.class;
4. -XX:MaxHeapFreeRatio = 70,设置Java虚拟机堆内存最大的空闲百分比,默认值为70,如java -XX:MaxHeapFreeRatio = 70 xxx.class;
5. -XX:NewRatio = 2,设置Young/Old内存的比例,如java -XX:NewRatio = 2 xxx.class;
6. -XX:NewSize = size,设置Young代内存的默认容量,如java -XX:NewSize = 64m xxx.class;
7. -XX:SurvivorRatio = 8,设置Young代中eden/survivor的比例,如java -XX:SurvivorRatio = 8 xxx.class;
8. -XX:MaxNewSize = size,设置Young代内存的最大容量,如java -XX:MaxNewSize = 128m xxx.class,当设置Young代的内存超过了-Xmx设置的大小时,Young设置的内存大小将不会起作用,JVM会自动将Young代内存的大小设置为与-Xmx设置的大小相等;
9. -XX:PermSize = size,设置Permanent代内存的默认容量,如java -XX:PermSize = 128m xxx.class;
10. -XX:MaxPermSize = size,设置Permanent代内存的最大容量,如java -XX:MaxPermSize = 256m xxx.class;
内存管理小技巧:
1. 尽量使用直接量,如使用String str = "hello"替代String str = new String("hello");
2. 使用StringBuilder和StringBuffer进行字符串连接,原因是因为使用String的连接会导致大量的临时对象;
3. 尽量释放无用对象的引用,如以下代码所示:
5. 以免在经常调用的方法、循环中创建Java对象,虽然这些对象会在方法或循环结束时被回收,但因不断创建,系统会不断地为这样的对象分配内存,在这种不断分配、回收操作中,程序的性能受到巨大影响;
6. 缓存经常使用的对象;
7. 尽量不要使用finalize方法,垃圾回收器会自行调用finalize方法,越多的finalize方法会导致垃圾回收器的负担越大;
1. 跟踪并监控每个java对象,当某个对象处理不可达状态时,回收该对象所占用的内存;
2. 清理分配、回收过程中所产生的内存碎片。
对于一个垃圾回收器的设计算法来说,大致有如下可供选择的设计:
1. 串行回收(Serial)和并行回收(Parallel):串行回收就是算管系统有多少个CPU,始终只用一个CPU来执行垃圾回收操作;并行回收就是把整个回收工作拆分成多个部分,每个部分由一个CPU负责,从而让多个CPU并行回收。并行回收的效率很高,但复杂度会增加,内存碎片也会增加;
2. 并发执行(Concurrent)和应用程序停止(Stop-the-world):应用程序停止的垃圾回收,会同时暂停应用;并发执行的垃圾回收不会暂停应用,但需要解决和应用程序的执行冲突,执行时需要更多的堆内存;
3. 压缩、不压缩和复制:复制——将堆内分成两个空间,从根访问每一个关联的可达对象,将空间A的所有可达对象复制到空间B,然后回收空间A;标记清除——也就是不压缩的回收方式,垃圾回收器先从根访问一次所有可达对象,将它们标记为可达状态,然后再遍历一次整个内存区域,把没有标记为可达对象的内存回收;标记压缩——即压缩的回收方式,可以说是复制回收和标记清除两种方式的综合,垃圾回收器先遍历一次内存区域,把所有可达对象标记为可达状态,在标记的过程中,把所有可达状态的对象放置到内存区域的同一块内存中,最后,把未标记的对象回收。
堆内存的分代回收——根据对象的生存时间长短,将对象分为不同的“代”,对不同代的对象采用不同的垃圾回收机制。垃圾回收器通常把对象分为三代,即Young、Old和Permanent,分代回收策略基于以下事实:
1. 绝大多数对象不会被长时间引用,这些对象在其Young期间就会被回收;
2. 很老的对象和很新的对象之间很少存在相互引用的关系。
Young代的回收通常采用复制算法。Young代由一个Eden区和两个Survivor区构成,绝大多数对象先分配到Eden区中(有一些大的对象可能会被直接分配到Old代中),Survivor区中的对象都至少在Young代中经历过一次垃圾回收,所以这些对象在被转移到Old代之前会先保留在Survivor区中。同一时间两个Survivor区中一个用来保存对象,而另一个是空的,用来在下次垃圾回收时保存Young代中的对象。每次复制就是将Eden和第一个Survivor中的可达对象复制到第二个Survivor区中,然后清空Eden和第一个Survivor区。
Old代的垃圾回收具有如下两个特征:
1. Old代垃圾回收的执行频率无需太高,因为很少有对象会死掉;
2. 每次对Old代执行垃圾回收需要更长的时间来完成。
基于以上考虑,垃圾回收器通常会采用标记压缩算法,也就是压缩式的垃圾回收算法来回收Old代的内存。
Permanent代主要用于装载Class、方法待信息,默认为64M,垃圾回收器通常不会回收Permanent代的对象。
Young和Old代的内存回收又分别被称为次要回收和主要回收。
部分与垃圾回收相关的附加选项:
1. -Xmx:设置Java虚拟机堆内存的最大容量,如java -Xmx256m xxx.class;
2. -Xms:设置Java虚拟机堆内存的初始容量,如java -Xms128m xxx.class;
3. -XX:MinHeapFreeRatio = 40,设置Java虚拟机堆内存最小的空闲百分比,默认值为40,如java -XX:MinHeapFreeRatio = 40 xxx.class;
4. -XX:MaxHeapFreeRatio = 70,设置Java虚拟机堆内存最大的空闲百分比,默认值为70,如java -XX:MaxHeapFreeRatio = 70 xxx.class;
5. -XX:NewRatio = 2,设置Young/Old内存的比例,如java -XX:NewRatio = 2 xxx.class;
6. -XX:NewSize = size,设置Young代内存的默认容量,如java -XX:NewSize = 64m xxx.class;
7. -XX:SurvivorRatio = 8,设置Young代中eden/survivor的比例,如java -XX:SurvivorRatio = 8 xxx.class;
8. -XX:MaxNewSize = size,设置Young代内存的最大容量,如java -XX:MaxNewSize = 128m xxx.class,当设置Young代的内存超过了-Xmx设置的大小时,Young设置的内存大小将不会起作用,JVM会自动将Young代内存的大小设置为与-Xmx设置的大小相等;
9. -XX:PermSize = size,设置Permanent代内存的默认容量,如java -XX:PermSize = 128m xxx.class;
10. -XX:MaxPermSize = size,设置Permanent代内存的最大容量,如java -XX:MaxPermSize = 256m xxx.class;
内存管理小技巧:
1. 尽量使用直接量,如使用String str = "hello"替代String str = new String("hello");
2. 使用StringBuilder和StringBuffer进行字符串连接,原因是因为使用String的连接会导致大量的临时对象;
3. 尽量释放无用对象的引用,如以下代码所示:
public void info() {
Object obj = new Object();
System.out.println(obj.toString());
System.out.println(obj.hasCode());
obj = null;
// 执行耗时,耗内存的操作
// 或者调用耗时,耗内存的方法
...
}
4. 尽量少用静态变量,垃圾回收器通常不会回收静态变量所占用的内存;
5. 以免在经常调用的方法、循环中创建Java对象,虽然这些对象会在方法或循环结束时被回收,但因不断创建,系统会不断地为这样的对象分配内存,在这种不断分配、回收操作中,程序的性能受到巨大影响;
6. 缓存经常使用的对象;
7. 尽量不要使用finalize方法,垃圾回收器会自行调用finalize方法,越多的finalize方法会导致垃圾回收器的负担越大;
8. 考虑使用SoftReference。