原则:无监控不调优
内存和GC原理
JAVA内存结构 堆、栈、永久区、程序计数器
一般需要优化的内存区为 堆
-
堆内存的构成 新生代、老年代
新生代分为eden(伊甸园)、survivor0、survivor1(幸存者;分为两块,新生代中的GC使用的是复制模式,交替复制清空)。这三块的容量关系一般为8:1:1 。新生代和老年代的比例一般为1:3有关GC(垃圾收集)机制,可参考: http://www.jianshu.com/p/5228e940cfd2
- JAVA new出一个对象ObjectA时,若它特别大,直接进入 老年代 ,其他都进入 新生代 中的eden。经过一次GC之后。若 新生代
中的对象还有引用指向ObjectA。它将进入 新生代 中的
survivor0 。第二次GC时,若还存在指向ObjectA的引用,ObjectA将复制到 survivor1 ,并清空 *survivor0 *。 这就是GC原理中的 复制模式,保证了有连续完整的整块内存来接收每一次回收后幸存对象。 - 在 新生代 中经过好几次的GC之后还没有被回收的对象,将进入 老年代
- JAVA new出一个对象ObjectA时,若它特别大,直接进入 老年代 ,其他都进入 新生代 中的eden。经过一次GC之后。若 新生代
-
java对象分配的优化
-
栈上分配 栈上可以存对象!没想到吧? 栈上分配模式将线程私有小对象(经过逃逸分析)直接放在栈上,无须GC回收。提升效率。虚拟机已有对应的默认配置,无须调整
ps: 逃逸分析 EscapeAnalysis
-
栈上分配 栈上可以存对象!没想到吧? 栈上分配模式将线程私有小对象(经过逃逸分析)直接放在栈上,无须GC回收。提升效率。虚拟机已有对应的默认配置,无须调整
/**
*逃逸分析测试类
*1.User对象在方法内被实例化时,对象未逃脱。被分配在栈内存中,不需要GC回收
*2.当User有外部引用时,对象逃脱。运行时间大大加长
*/
public class PartionOnStack {
class User{
public int id;
public String name;
}
User user; //有外部引用时,对象逃脱
public void foo() {
User user = new User(); //对象未逃脱。全部被分配在栈内存中
user.id=1;
user.name="66666";
}
public static void main(String[] args) {
System.out.println("start-----------");
long beginTime=System.currentTimeMillis();
PartionOnStack pos=new PartionOnStack();
for(int i=0;i<100000000;i++)
{
pos.foo();
}
long endTime=System.currentTimeMillis();
System.out.println("总共运行----"+(endTime-beginTime)+"ms");
}
}
- 线程本地分配(Thread Local Allacation Buffer) 默认占用1%eden区,多线程的时候不竞争eden就可以申请空间,提高效率。虚拟机已有对应的默认配置,无须调整
- 分配功能关闭和开启 -XX:+DoEscapeAnalysis(逃逸分析开启)-XX:+EliminateAllocations(标量替换开启) -XX:+UseTLAB(使用本地缓存) -XX:+PrintGC(打印GC信息)。jvm虚拟机默认都是开启的。如果依次设置为“-” 不开启状态。会发现效率越来越低。全开启状态下。若方法内部都为私有小对象,GC一次都不会运行,效率大大提高。
调优方式
-
堆内存最大最小值设置 :
-Xmx10M
堆的最大值,-Xms10M
堆的起始值 .调优时建议最小值跟最大值一致。直接占用到最大。省去程序自己去分配去GC的过程,这些过程本身也会影响性能。 - 堆内存参数调整 用VisualVM观察虚拟机堆信息
-
OutOfMemory的监控 配置jvm参数
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/sam/Downloads
OOM发生时,在配置的路径下可找到dump文件。导入到VisualVM中可查看具体信息
-
栈空间:栈空间配置
-Xss128k
栈空间调大可增加递归调用次数。每一次的函数调用生成栈帧,请求的栈深度大于配置的最大深度时就会StackOverFlowError,在递归调用时最有可能溢出。栈空间调小,允许存在更多的线程,并发性能会更好。根据实际情况调节。 -
典型tomcat优化配置
set JAVA_OPTS=- -Xms4g //根据实际服务器情况
- -Xmx4g //根据实际服务器情况
- -Xss512k
- -XX:+AggressiveOpts //开启全部jvm的默认优化选项
- -XX:+UseBiasedLocking //偏向锁 一般情况都是需要配置
偏向锁的目的是为了在无锁竞争的情况下避免在锁获取过程中执行不必要的CAS原子指令;现有的CAS原子指令虽然相对于重量级锁来说开销比较小但还是存在非常可观的本地延迟。而偏向锁则针对拥有当前锁的线程,允许其在竞争不存在的情况下,直接进入同步的代码块,无需同步操作,从而获取了相当的性能提升。
- -XX:PermSize=64M //永久区大小 jdk8之后改为了metaspace
- -XX:MaxPermSize=300M //永久区最大值 类多的情况下,设大
- -XX:+DisableExplicitGC //不能显式调用GC System.gc() 因为程序中调用GC方法将破坏配置中的优化选项
- -XX:+UseConcMarkSweepGC //使用CMS缩短响应时间,并发收集,低停顿
- -XX:+UseParNewGC //并行收集新生代垃圾
- -XX:+CMSParallelRemarkEnabled //在使用UseParNewGC的情况下,尽量减少mark的时间
- -XX:+UseCMSCompactAtFullCollection //使用并发收集器时,开启老年代的压缩,使碎片减少
- -XX:LargePageSizeInBytes=128m //内存分页大小对性能的提升
- -XX:+UseFastAccessorMethods //get/set方法转为本地代码
-
压力测试工具
- JMeter安装(Mac):brew install jmeter --with-plugins 运行:open /usr/local/bin/jmeter
- 测试方式请百度谷歌
- 垃圾收集算法
- 垃圾收集器选择