Java内存与垃圾回收机制

        垃圾回收机制主要完成以下两件事情:
        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。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值