⑨. 堆空间参数总结
①. -XX:+PrintFlagsInitial : 查看所有的参数的默认初始值
②. -XX:+PrintFlagsFinal : 查看所有的参数的最终值(可能会存在修改(:表示修改了),不再是初始值)
③. 具体查看某个参数的指令:
(jps:查看当前运行中的进程
jinfo -flag SurvivorRatio 进程id)
④. -Xms:初始堆空间内存 (默认为物理内存的1/64)
⑤. -Xmx:最大堆空间内存(默认为物理内存的1/4)
⑥. -Xmn:设置新生代的大小。(初始值及最大值)
⑦. -XX:NewRatio:配置新生代与老年代在堆结构的占比
(默认:-XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3
可以修改-XX:NewRatio=4,表示新生代占1,老年代占4,新生代占整个堆的1/5)
⑧. -XX:SurvivorRatio:设置新生代中Eden和S0/S1空间的比例
(Eden空间和另外两个Survivor空间缺省所占的比例是8:1:1)
⑨. -XX:MaxTenuringThreshold:设置新生代垃圾的最大年龄
⑩. -XX:+PrintGCDetails:输出详细的GC处理日志
(如下这两种方式是简单的打印
打印gc简要信息:① -XX:+PrintGC ② -verbose:gc)
⑩①. -XX:HandlePromotionFailure:是否设置空间分配担保
(JDK6之后,只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进
行Minor GC,否则将进行Full GC)
内存分配策略(或对象提升(Promotion)规则)
(1). 在繁盛Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间
====如果大于,则此次Minor GC是安全的
====如果小于,则虚拟机会检查查看-XX:HandlePromotionFailure设置值是否允许担保失败
=====如果 HandlePromotionFailure=true,那么会继续检查老年代最大可用连续空间是否大于历次晋级到老年代的对象的平均大小
========如果大于,则尝试进行一次Minor GC,但是这次Minor GC依然是有风险的
========如果小于,则改为一次Full GC
=====如果HandlePromotionFailure=false,则改为进行一次Full GC
(2). 在JDK6 Update24之后,HandlePromotionFailure参数不会再影响虚拟机的空间分配担保策略,观察OpenJDK中源码变化,虽然源码中还定义了HandlePromotionFailure参数,但是在代码中已经不会再使用它
(3). JDK6 Update24之后规则变为只有老年代的连续空间大于新生代对象总大小或者历次晋身的平均大小就会进行Minor GC,否则将进行Full GC
⑩. 逃逸分析
- ①. 如何将堆上的对象分配到栈,需要使用逃逸分析手段
- 当一个对象在方法中被定义后,对象只在方法内部使用(这里关注的是这个对象的实体),则认为没有发生逃逸。
- 当一个对象在方法中被定义后,它被外部方法所引用,则认为发生逃逸。例如作为调用参数传递到其他地方中
- ②. 代码演示
//(1). 没有发生逃逸的对象,则可以分配到栈上,随着方法执行的结束,栈空间就被移除 public void my_method() { V v = new V(); // use v // .... v = null; } //(2). 下面代码中的 StringBuffer sb 发生了逃逸 public static StringBuffer createStringBuffer(String s1, String s2) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); return sb; } //如果想要StringBuffer sb不发生逃逸,可以这样写 public static String createStringBuffer(String s1, String s2) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); return sb.toString(); }
③. 逃逸分析的举例
/** * 逃逸分析 * * 如何快速的判断是否发生了逃逸分析,大家就看new的对象实体是否有可能在方法外被调用。 */ public class EscapeAnalysis { public EscapeAnalysis obj; /* 方法返回EscapeAnalysis对象,发生逃逸 */ public EscapeAnalysis getInstance(){ return obj == null? new EscapeAnalysis() : obj; } /* 为成员属性赋值,发生逃逸 */ public void setObj(){ this.obj = new EscapeAnalysis(); } //思考:如果当前的obj引用声明为static的? 仍然会发生逃逸。 /* 对象的作用域仅在当前方法中有效,没有发生逃逸 */ public void useEscapeAnalysis(){ EscapeAnalysis e = new EscapeAnalysis(); } /* 引用成员变量的值,发生逃逸 */ public void useEscapeAnalysis1(){ EscapeAnalysis e = getInstance(); //这个e对象,本身就是从外面的方法逃逸进来的 //getInstance().xxx()同样会发生逃逸 } }
④. 在JDK1.7版本之后,HotSpot中默认就已经开启了逃逸分析
如果使用的是较早的版本,开发人员则可以通过:
选项“-XX:+DoEscapeAnalysis"显式开启逃逸分析
通过选项“-XX:+PrintEscapeAnalysis"查看逃逸分析的筛选结果
- 结论: ⑤. 开发中能使用局部变量的,就不要使用在方法外定义
- ⑥. 使用逃逸分析,编译器可以对代码做如下优化:
栈上分配:将堆分配转化为栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会发生逃逸,对象可能是栈上分配的候选,而不是堆上分配
同步省略:如果一个对象被发现只有一个线程被访问到,那么对于这个对象的操作可以不考虑同步。
分离对象或标量替换:有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在CPU寄存器中