上图中obj1,obj2,obj3,obj4可达,obj5,obj6不可达(可被GC回收)。 html
1. 虚拟机栈中局部变量引用的对象。 java
2. 类静态属性引用的对象。 算法
3. 常量引用的对象。 缓存
4. JNI中引用的对象。 多线程
java中有四种引用类型(由强到弱): 并发
1. 废弃常量(如无引用的常量字符串)。 oracle
2. 无用的类。 jvm
1. 该类的全部实例已经被回收。 jsp
2. 加载该类的ClassLoader已经被回收。 spa
3. 该类的java.lang.Class对象没有被引用,没法任何地方经过反射访问该类。
简单方便,内存碎片化严重
无内存碎片化, 浪费可用内存
无内存碎片化,充分利用可用内存
根据对象存活特性,合理使用不一样回收算法,商业JVM都使用该回收算法。
1. 初始标记:标记GC Roots可关联的对象,会暂停用户线程。
2. 并发标记:GC Roots Tracing过程,用户线程并行。
3. 从新标记:从新标记因为在并发标记过程当中用户线程致使的对象状态变化,会暂停用户线程。
4. 并发清理:清理可回收对象,用户线程并行。
这些收集器都一些可控参数,根据实际场景来调整,这里是官方文档:
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
内存分配回收实例:
/** * 内存分配回收 * -XX:+PrintGCDetails 打印GC信息 * -Xms12M 起始堆大小 * -Xmx12M 最大堆大小 * -Xmn6M 年轻代大小 * -XX:SurvivorRatio=6 Eden:S0:S1 = 4:1:1 * * jvm args: * -XX:+PrintGCDetails -Xms12M -Xmx12M -Xmn6M -XX:SurvivorRatio=4 */ public class MemoryAllocate { private static final int _1M = 1024*1024; public static void main(String[] args) { byte[] b1 = new byte[_1M]; // allocate 1M byte[] b2 = new byte[_1M*2]; // allocate 2M byte[] b3 = new byte[_1M]; // allocate 1M, 发生年轻代GC(Minor GC) } }gc信息以下:
1.Minor GC:发生在年轻代,速度快,频繁。
2.Full GC/Major GC:发生在老年代, 通常也伴随Minor GC, 速度通常比Minor GC慢10倍以上。
/** * PretenureSizeThreshold * -XX:+PrintGCDetails 打印GC信息 * -Xms12M 起始堆大小 * -Xmx12M 最大堆大小 * -Xmn6M 年轻代大小 * -XX:SurvivorRatio=6 Eden:S0:S1 = 4:1:1 * -XX:PretenureSizeThreshold 对象超过该值,直接进入老年代,单位B * -XX:UseParNewGC 使用UserParNew收集器 * jvm args: * -XX:UseParNewGC -XX:+PrintGCDetails -Xms12M -Xmx12M -Xmn6M -XX:SurvivorRatio=4 -XX:PretenureSizeThreshold=2097152 */ public class PretenureSizeThreshold { private static final int _1M = 1024*1024; public static void main(String[] args) { // 分配2M, 大于PretenureSizeThreshold, 直接进入老年代 byte[] b1 = new byte[_1M * 3]; } }
上面就基本介绍了jvm分配回收内存策略。
不吝指正。