虚拟cpu
cpu可分为基于寄存器的和基于栈的两种。x86是基于寄存器的cpu,而java虚拟机是一种基于栈的(虚拟)cpu,要运算的数据存放在操作数栈(Operand Stack),指令一般不需指明数据的位置,比如iadd指令就是把栈顶两个整数弹出并把相加结果入栈。Android Dalvik虚拟机是基于寄存器的,但可以执行java字节码。
运行时内存区域
-
程序计数器
- 每个线程有一个,指向下一条要执行的指令。 虚拟机栈
- 也是线程私有的,每进入一个方法就会创建一个栈帧(Stack Frame)。每个栈帧里面又会包含局部变量表、操作数栈、动态链接、返回地址等。其中每个部分的大小是在编译期确定的,即一个栈帧的大小是在编译期确定的。局部变量表的第一个slot里存放的就是this指针。 本地方法栈 java堆 方法区
- 存放已加载的类信息。也被成为永久代(Permanent Generation),可用-XX:maxPermSize指定大小。方法区里面有一块被称为运行时常量池(Runtime Constant Pool),存放常量(比如intern的String)。如果动态生成的类太多,则会造成永久区溢出:OutOfMemoryError: PermGen Space。-XX:MaxPermSize=64M可以满足绝大多数应用的需求,如果还不够就设成128M,如果还是不够就应该重构系统,比如使用专门的classloader等。(如果一个类的所有对象都已经被销毁,而且该类的classloader也被销毁,则该class会被回收。而常量池会自动回收。) 直接内存
- 用于NIO,文件通道。
不同引用类型
- StrongRefence。
- SoftReference在内存将满时会被回收,适合用来实现cache。
- WeakReference只要被垃圾回收器发现就会被回收,即最多生存到下一次垃圾回收之前。
- PhantomReference(虚引用)最弱的引用,仅用来跟踪垃圾回收过程。
垃圾收集算法
-
标记-清除算法
- 标记要回收的对象,然后释放,会产生很多碎片。 复制算法
- 将内存分两半,将活着的对象复制到另一半。适合新生代,新生代98%的对象时朝生夕死的,所以不需要1:1的比例。Hotspot虚拟机默认是8:1(-XX:SurvivorRation=8),大的那一块叫Eden(一次gc都还没经历的对象),小的那块叫Survivor,Survivor又平分为S0和S1。新创建的对象放在eden区,如果经历一次gc之后还活着,则进入s0或s1。s0和s1是对等的,每次gc之后交换角色。新生代=Eden+S0+S1,新生代的大小-Xmn128m。 标记-整理算法
- 适用于老年代,类似标记-清除算法,会移动对象以避免碎片。 分代收集算法
- 将对象划分为新生代(Young generation)和老年代(Tenured generation),使用不同的回收算法。如果一个对象经过多次gc仍然存在,则进入老年代。新生代采取复制算法(包括Serial,ParNew,Parallel Scavenge收集器),老年代采取复制-整理算法。
hotspot使用的垃圾收集器
-
Serial收集器
- client模式下新生代的默认收集器,单线程,stop the world。 ParNew收集器
- Serial的并行版本,stop the world。在server模式下,如果老年代使用CMS,则新生代首选ParNew。 Parallel Scavenge收集器
- 关注最大停顿时间和吞吐量,stop the world。吞吐量=用户代码时间/(用户代码+垃圾回收时间)。会自定调整新生代大小等参数。停顿时间短适用于交互程序;吞吐量大则适用于非交互程序。 Serial Old收集器
- Serial的老年版本,标记-整理算法,stop the world。Client模式下的默认收集器。 Parallel Old收集器
- Parallel Scavenge的老年版本,标记-整理算法,stop the world。 CMS(Concurrent Mark Sweep)收集器
- 以最短停顿时间为目标,标记-清除算法,适用于B/S服务器。可以与用户线程并行工作,即不需要stop the world。 G1收集器
- 最新的收集器,有待时间检验。
查看GC日志
参数 -verbose:gc -XX:+PrintGCDetails
[GC [DefNew: 3324k->152K(3712K), 0.002 secs] 3324K->152K(11094K), 0.003 secs]
其中里层括号表示DefNew(也就是Serial)收集器的回收情况,回收前->回收后(总大小)。外层表示总的回收情况。新生代一般称为Minor GC,老年代称为Major GC或Full GC,老年代GC时一般也会伴随Minor GC。
内存分配与回收策略
-
对象优先在Eden分配
- -Xmn10M设置新生代大小,-XX:SurvivorRation=8设置新生代中Eden与Survivor的比例。 大对象直接进入老年代
- -XX:PretenureSizeThreshold=3145728大于此数值的对象直接分配在老年代。 长期存活对象进入老年代
- -XX:MaxTenuringThreshold=15年龄超过此数值的对象进入老年代,默认15。 动态年龄判定
- 如果在survivor中的相同年龄的对象大小占了survivor空间的一半以上,则此年龄以上的对象直接进入老年代,而无需等待MaxTenuringThreshold。 空间分配担保
- -XX:+HandlePromotionFailure。Minor GC时,如果survivor空间不够,就会借用老年代的空间;如果老年剩余空间也不够,就会进行full GC。
调试工具
jps查看java进程号
jstat
查看虚拟机的统计信息,如各空间大小,垃圾回收情况,类加载情况等。
jinfo
查看或修改虚拟机的运行参数、环境变量等。
jmap导出java堆
jmap -histo pid
查看对象统计信息。
jmap -dump:format=b,file=dump.bin pid
导出堆。
jhat
在浏览器中查看dump文件。
jstack
查看调用栈,可用来检查死锁。
hprof
查看各函数的cpu使用率
JConsole
图形化工具,监测堆、线程、死锁等信息
VisualVM新一代图形化工具
目前主推的一站式监控工具,基于netbeans开发,all-in-one。