内存分配策略:
优先分配到eden
大对象直接分配到老年代
长期存活的对象分配到老年代
空间分配担保
动态对象年龄判断
1.优先分配到eden
-verbose:gc -XX:+PrintGCDetails 默认是Parallel Scavenge 收集器,标志为:PSYoungGen
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC 指定Serial收集器 ,标志为:def new generation
public class Main {
public static void main(String[] args) {
byte [] m=new byte[4*1024*1024];
}
}
代码中为byte数组分配了4M的空间,运行结果:大致分析
堆
新生代总计内存33M,使用了6M,eden区域 内存总计30M,21%的空间被使用,tenured generation养老区内存总计75M
更改代码如下:
public class Main {
public static void main(String[] args) {
byte [] m=new byte[40*1024*1024];
}
}
运行发现:大对象被直接分配到了tenured generation,eden区域的使用内存减少
指定jvm参数:
-verbose:gc(-verbose:gc 是 -XX:+PrintGC的别名,打印gc运行信息)
-XX:+PrintGCDetails(详细打印jvm运行日志)
-XX:+UseSerialGC(指定serial GC垃圾回收器)
-Xms20M(设置JVM最大可用内存为20M)
-Xmx20M(用来设置程序初始化的时候内存栈的大小,增加这个值的话你的程序的启动性能会得到提高
此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存)
-Xmn10M(指定新生代内存10M)
-XX:SurvivorRatio=8 (指定新生代的eden为8M)
-XX:PretenureSizeThreshold=6M(指定6M以上的就判定为大对象,大对象直接分配到tenured区)
public class Main {
public static void main(String[] args) {
//m1,m2,m3被分配到了tenured区域 8.192M*58%=4M
byte [] m1=new byte[2*1024*1024];
byte [] m2=new byte[2*1024*1024];
byte [] m3=new byte[2*1024*1024];
//m4被分配到了eden区,10M*60%=6M
byte [] m4=new byte[4*1024*1024];
//调用gc垃圾回收,回收from space和to space 垃圾区域所占的内存
System.gc();
}
}
[GC (Allocation Failure) [DefNew: 7129K->530K(9216K),] 7129K->6674K(19456K),
<p>调用allocation gc,回收了eden的7M空间,6M的三个对象仍存活,故未被清理掉</p>
[Full GC (System.gc()) [Tenured: 6144K->6144K(10240K)] 10930K->10768K(19456K), [Metaspace: 2654K->2654K(1056768K)]
<h1>调用full gc,清理掉from space的1M垃圾空间</h1>
Heap
def new generation total 9216K, used 4788K
eden space 8192K, 58% used
from space 1024K, 0% used
to space 1024K, 0% used
tenured generation total 10240K, used 6144K
the space 10240K, 60% used
Metaspace used 2660K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 286K, capacity 386K, committed 512K, reserved 1048576K
长期存活的对象进入老年代
-XX:MaxTenuringThreshold 15
Age 1+1+1
空间分配担保:
-XX:+HandlePromotionFailure(先衡量有无足够的能力【空间】满足你的内存分配的需求,+是开启,-是禁用,老年代的空间并不能容纳下所有的新生代的对象的空间)
逃逸分析与栈上分配
栈上分配:方法执行需要创建一个栈帧,方法执行,栈帧进栈出栈,栈区域是根据方法的执行来进行分配与释放,不需要垃圾回收器来处理,性能很高
为逃逸的对象的可在栈上进行分配,分析出对象的作用域(方法体内部,方法内有效,它为逃逸,故可分配在栈上)
逃逸分析的情况:
public class StackAllocation {
public StackAllocation obj;
/* 方法返回StackAllocation对象,放生逃逸*/
public StackAllocation getInstance() {
return obj==null?new StackAllocation():obj;
}
/* 为成员属性赋值,放生逃逸*/
public void setObj() {
this.obj=new StackAllocation();
}
/* 对象的作用域仅在当前方法中有效,没有发生逃逸*/
public void useStackAllcation() {
/*在方法中定义对象,则对象会被直接分配到栈中,随着方法结束,栈内存就会被移出,内存回收,对象也随之销毁,性能较高
能局部创建对象,就局部创建对象*/
StackAllocation s =new StackAllocation();
}
/* 引用成员变量的值,发生逃逸*/
public void useStackAllocation2() {
StackAllocation s=getInstance();
}
}