JVM运行时数据
1. 结构图
2. Heap
Java堆是整个JVM共享的,堆的大小取决于配置项(-Xmx-Xms)来指定,堆的最大内存取决于操作系统和硬件环境,比如2G/32位系统,一般也就用800M左右,配多了操作系统会禁止JVM启动的。同时,堆空间并不需要物理上连续的空间,只要逻辑上连续即可。
堆空间主要分为2个区域,一个叫做Java堆,用于存放各种实例化对象,是垃圾回收机制重点关注的区域,因为对象的申请和释放非常频繁。另一个叫做方法区,又叫非堆,主要用于存放各种被虚拟机加载的信息,如类信息(类名、方法名、属性、接口、版本等)、常量、静态变量、即时编译后的代码等。
2.1 对象区Heap space
对象区又称为Java堆,是最大的一块内存,用于存放对象实例,几乎所有的对象都在这里分配内存,是GC重点管理的区域。
可以通过增加如下配置用于在内存溢出时dump出当前的内存堆转储快照:-XX:+HeapDumpOnOutOfMemoryError
2.2 方法区PermGen space
又称为非堆,或者永久区,用于存放类相关信息、常量、静态变量、即时编译后的代码等数据。
3. Stack
栈是线程互相独立的,每个线程都有属于自己的栈。
栈中主要保存运行信息,比如压栈的情况,以及局部变量表,局部变量表的大小,在编译器就已经生成和确定。
此外,栈可以分为java虚拟机栈和本地方法栈,前者用于存放java的数据,后者用于存放JNI调用情况和数据,在hotspot虚拟机中,二者合二为一,并不做区分。
4. Direct memory
直接内存,为NIO服务,是直接向操作系统申请jvm以外的内存,供NIO使用,这些内存不受垃圾回收机制控制
5. 关于异常
| OutOfMemoryError | StackOverFlowError | ||
| 是否存在 | 出错场景 | 是否存在 | 出错场景 |
程序计数器 | × | 无 | × | 无 |
Java虚拟机栈/本地方法栈 | √ | 栈空间局部变量太多,扩展内存失败 | √ | 压栈太深,如错误的递归调用 |
Java堆 | √ | 实例对象太多,且堆无法扩展 | × |
|
方法区,包括运行时常量池 | √ | 加载的类太多,或常量太多,且空间无法扩展 | × |
|
直接内存 | √ | 操作系统内存不足 | × |
|
6. jvm调优参数
JVM参数配置
1: heapsize
a:-Xmx<n>
指定 jvm 的最大 heap 大小 , 如 :-Xmx=2g
b:-Xms<n>
指定 jvm 的最小 heap 大小 , 如 :-Xms=2g , 高并发应用, 建议和-Xmx一样, 防止因为内存收缩/突然增大带来的性能影响。
c:-Xmn<n>
指定 jvm 中 NewGeneration 的大小 , 如 :-Xmn256m。这个参数很影响性能,如果你的程序需要比较多的临时内存,建议设置到512M,如果用的少, 尽量降低这个数值, 一般来说128/256足以使用了。
d:-XX:PermSize=<n>
指定 jvm 中 PermGeneration 的最小值 , 如 :-XX:PermSize=32m。这个参数需要看你的实际情况,。可以通过jmap 命令看看到底需要多少。
e:-XX:MaxPermSize=<n>
指定 Perm Generation 的最大值 , 如:-XX:MaxPermSize=64m
f:-Xss<n>
指定线程桟大小 , 如 :-Xss128k, 一般来说,webx框架下的应用需要256K。 如果你的程序有大规模的递归行为,请考虑设置到512K/1M。这个需要全面的测试才能知道。不过,256K已经很大了。这个参数对性能的影响比较大的。
g:-XX:NewRatio=<n>
指定 jvm 中 OldGeneration heap size 与 New Generation 的比例 , 在使用 CMS GC 的情况下此参数失效 , 如 :-XX:NewRatio=2
h:-XX:SurvivorRatio=<n>
指 定 New Generation 中 Eden Space 与一个 SurvivorSpace 的 heap size 比例 ,-XX:SurvivorRatio=8, 那么在总共 New Generation 为 10m 的情况下 ,EdenSpace 为 8m
i:-XX:MinHeapFreeRatio=<n>
指定 jvm heap 在使用率小于 n 的情况下 ,heap 进行收缩,Xmx==Xms 的情况下无效 , 如 :-XX:MinHeapFreeRatio=30
j:-XX:MaxHeapFreeRatio=<n>
指定 jvm heap 在使用率大于 n 的情况下 ,heap 进行扩张,Xmx==Xms 的情况下无效 , 如 :-XX:MaxHeapFreeRatio=70
k:-XX:LargePageSizeInBytes=<n>
指定 Java heap 的分页页面大小 , 如:-XX:LargePageSizeInBytes=128m
2:garbage collector
a:-XX:+UseParallelGC
指 定在 New Generation 使用 parallelcollector, 并行收集 , 暂停 app threads, 同时启动多个垃圾回收 thread, 不能和 CMS gc 一起使用 . 系统吨吐量优先 , 但是会有较长长时间的 app pause, 后台系统任务可以使用此 gc
b:-XX:ParallelGCThreads=<n>
指定 parallelcollection 时启动的 thread 个数 , 默认是物理 processor 的个数 ,
c:-XX:+UseParallelOldGC
指定在 Old Generation 使用 parallelcollector
d:-XX:+UseParNewGC
指定在 New Generation 使用 parallelcollector, 是 UseParallelGC 的 gc 的升级版本 , 有更好的性能或者优点 , 可以和 CMS gc 一起使用
e:-XX:+CMSParallelRemarkEnabled
在使用 UseParNewGC 的情况下 , 尽量减少 mark 的时间
f:-XX:+UseConcMarkSweepGC
指 定在 Old Generation 使用 concurrentcmark sweep gc,gc thread 和 app thread 并行 ( 在 init-mark 和 remark 时 pause app thread). app pause 时间较短 , 适合交互性强的系统 , 如 web server
g:-XX:+UseCMSCompactAtFullCollection
在使用 concurrent gc 的情况下 , 防止 memoryfragmention, 对 live object 进行整理 , 使 memory 碎片减少
h:-XX:CMSInitiatingOccupancyFraction=<n>
指示在 old generation 在使用了 n% 的比例后 , 启动 concurrentcollector, 默认值是 68, 如 :-XX:CMSInitiatingOccupancyFraction=70
i:-XX:+UseCMSInitiatingOccupancyOnly
指示只有在 old generation 在使用了初始化的比例后concurrent collector 启动收集
3:others
a:-XX:MaxTenuringThreshold=<n>
指 定一个 object 在经历了 n 次 young gc 后转移到 oldgeneration 区 , 在 linux64 的 java6 下默认值是 15, 此参数对于 throughput collector 无效 , 如 :-XX:MaxTenuringThreshold=31
b:-XX:+DisableExplicitGC
禁止 java 程序中的 full gc,如 System.gc() 的调用. 最好加上么,防止程序在代码里误用了。对性能造成冲击。
c:-XX:+UseFastAccessorMethods
get,set 方法转成本地代码
d:-XX:+PrintGCDetails
打应垃圾收集的情况如 :
[GC15610.466: [ParNew: 229689K->20221K(235968K), 0.0194460 secs]1159829K->953935K(2070976K), 0.0196420 secs]
e:-XX:+PrintGCTimeStamps
打应垃圾收集的时间情况 , 如 :
[Times:user=0.09 sys=0.00, real=0.02 secs]
f:-XX:+PrintGCApplicationStoppedTime
打应垃圾收集时 , 系统的停顿时间 , 如 :
Totaltime for which application threads were stopped: 0.0225920 seconds
JVM参数设置大全
http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
JVM参数设置大全压缩版
http://blogs.sun.com/watt/resource/jvm-options-list.html
7. 其他知识点
7.1 关于动态向常量池中添加常量和String的intern()方法
常量池中的常量并非一定要预置在代码中,运行期间也可能将新的常量放入常量池中,String的intern()方法就是利用这个特性。
在JDK1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中的字符串实例的引用,所以这个引入跟普通的stringBuffer生成的对象引用肯定是不一样的,即时文本内容一样。
7.2 何时会出现加载的类太多?
通常情况下,大量的JSP或者动态生成jsp的应用、基于OSGi的应用、动态代理(可以基于cglib,如spring AOP)等,均会动态生成class字节码,导致大量的字节码被加载,可能造成永久去溢出