jvm
2017-03-20 10:01:54 0 举报
AI智能生成
JVM知识结构图
作者其他创作
大纲/内容
jvm实践
java 常用工具及常用命令
jmap
jmap [option] <pid>
(to connect to running process)
jmap [option] <executable <core>
(to connect to a core file)
jmap [option] [server_id@]<remote server IP or hostname>
(to connect to remote debug server)
heap 堆信息
-dump:<dump-options> to dump java heap in hprof binary format
dump-options:
live dump only live objects; if not specified,
all objects in the heap are dumped.
format=b binary format
file=<file> dump heap to <file>
dump文件分析内存情况,会造成虚拟机停止
jmap -dump:format=b,file=dumpFileName pid
jstat
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
generalOption
-help 帮助
-options 打印选项
2.outputOptions
输出选项
-h n 每n个样本,显示header一次
-t n 在第一列显示时间戳列,时间戳时从jvm启动开始计算
-Jjvmoption 传递jvm选项
-statOption 决定统计什么信息
(1)class:统计classloader的行为
Column Description
Loaded 被读入类的数量
Bytes 被读入的字节数(K)
Unloaded 被卸载类的数量
Bytes 被卸载的字节数(K)
Time 花费在load和unload类的时间
(3)gc:统计gc行为
Column Description
S0C 当前S0的容量 (KB).
S1C 当前S1的容量 (KB).
S0U S0的使用 (KB).
S1U S1的使用 (KB).
EC 当前eden的容量(KB).
EU eden的使用 (KB).
OC 当前old的容量(KB).
OU old的使用 (KB).
PC 当前perm的容量 (KB).
PU perm的使用 (KB).
YGC young代gc的次数
YGCT young代gc花费的时间
FGC full gc的次数
FGCT full gc的时间
GCT 垃圾收集收集的总时间
(11)gcutil:垃圾收集统计
Column Description
S0 S0使用百分比
S1 S1使用百分比
E eden使用百分比
O old使用百分比
P perm使用百分比
YGC 年轻代gc次数
YGCT 年轻代gc时间
FGC full gc次数
FGCT full gc时间
GCT 垃圾收集总时间
(5)gccause:垃圾收集统计,包括最近引用垃圾收集的事件,基本同gcutil,比gcutil多了两列
Column Description
LGCC 最近垃圾回收的原因.
GCC 当前垃圾回收的原因.
sudo -u ads jstat -gccause -t 10110 2500 6
(9)gcoldcapacity:统计旧生代的大小和空间
Column Description
OGCMN 最小年老代容量 (KB).
OGCMX 最大年老代容量 (KB).
OGC 当前年老代容量 (KB).
OC 当前年老代空间 (KB).
YGC 年轻代gc次数
FGC full gc次数
FGCT full gc时间
GCT 垃圾收集总时间
(6)gcnew:统计新生代的行为
Column Description
S0C 当前S0空间 (KB).
S1C 当前S1空间 (KB).
S0U S0空间使用 (KB).
S1U S1空间使用 (KB).
TT Tenuring threshold.
MTT 最大的tenuring threshold.
DSS 希望的Survivor大小 (KB).
EC 当前eden空间 (KB).
EU eden空间使用 (KB).
YGC 年轻代gc次数
YGCT 年轻代垃圾收集时间
jps
虚拟机PID查询
jinfo
jinfo(Configuration info for java)的作用是实时地查看和调整虚拟机的各项参数
jstack
jstack(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照(一般称为threaddump或javacore文件)
JVM参数
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:d:\gc.log
性能选项
分支主题
分支主题
分支主题
调试选项
分支主题
分支主题
分支主题
分支主题
分支主题
分支主题
行为选项
分支主题
分支主题
分支主题
分支主题
分支主题
MAT:内存分析
eclipse插件
http://www.eclipse.org/mat/downloads.php
优化实践
1 选用高效率的垃圾收集器
2 查看GC日志 修改JVM参数
组成图
组成图
组成
PC寄存器-PC Register
定义:每个线程启动的时候,都会创建一个PC(Program Counter ,程序计数器)寄存器。PC寄存器里保存有当前正在执行的JVM指令的地址。程序计数器是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器
在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。每个线程都有自己的计数器。
线程当前执行的方法称为当前方法,PC寄存器用来存放当前方法中当前执行的字节码指令的地址,如果当前方法是本地方法(Native),那么寄存器存放undefined。
此内存区域是唯一一个Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
本地方法栈-Native Method Stack
定义:本地方法栈与虚拟机栈的作用非常相似,区别就是虚拟机栈为虚拟机执行Java方法,本地方法栈则是为虚拟机使用到的Native方法服务。
虚拟机规范中对本地方法栈中的方法使用的语言、使用方式和数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至与虚拟机栈合二为一
异常:
1.如果线程中计算所需的本地方法栈大于允许范围,jvm会抛出StackOverflowError。
2.如果本地方法栈能动态扩展,当没有足够的内存分配给所尝试的扩展,或者没有足够的内存分配给新线程中创建的初始本地方法栈,jvm就会抛出OutOfMemoryError。
堆-Heap
定义:Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
组成
新生代(Young Generation)
Eden
大部分新生的对象创建区
超过一定内存空间会转移到 Survivor区
From Space(S0)
To Space(S1)
设置情况
响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
年老代
年轻代的对象如果能够挺过数次收集,就会进入年老代
gc算法
参数配置
-Xms -Xmx 堆内存设置尽量保证一致
在32Bit操作系统上有4G的限制,一般来说Windows下为2G,而Linux 下为3G;64Bit的就没有这个限制。
JVM初始分配的内存由-Xms指定,默认是物理内存的1/64但小于1G。
JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4但小于1G。
默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制,可以由 -XX:MinHeapFreeRatio=指定。
默认空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制,可以由 -XX:MaxHeapFreeRatio=指定。
-XX:NewRatio=
设置Young与Old的大小比例,-server时默认为1:2,但实际上young启动时远低于这个比率。如果信不过JVM,也可以用 -Xmn硬性规定其大小,有文档推荐设为Heap总大小的1/4。建议1:3
-XX:MaxTenuringThreshold=
设置熬过年轻代多少次收集后移入老人区,CMS中默认为0,熬过第一次GC就转入
方法区-Method Area
定义:方法区也是所有线程共享区,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。
存储内容
类型信息
这个类型的全限定名
这个类型的直接超类的全限定名
这个类型是类类型还是接口类型
这个类型的访问修饰符
任何直接超接口的全限定名的有序列表
字段信息
字段名
字段类型
字段的修饰符
方法信息
方法名
方法返回类型
方法参数的数量和类型(按照顺序)
方法的修饰符
方法列表(Method Tables )
常量池
异常错误
" java.lang.OutOfMemoryError: PermGen space
-XX:PermSize=10M -XX:MaxPermSize=10M
栈-Stack
定义:JVM的内存指令区
生命周期
Java栈是与每一个线程关联的,JVM在创建每一个线程的时候,会分配一定的栈空间给线程。它主要用来存储线程执行过程中的局部变量,方法的返回值,以及方法调用上下文。栈空间随着线程的终止而释放。不存在垃圾回收。
特点
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
4.先进后出规则
5.速度快,仅次于CPU寄存器
栈示意图
栈示意图
最大线程数
(MaxProcessMemory – JVMMemory – ReservedOsMemory) / (ThreadStackSize)
MaxProcessMemory 指的是一个进程的最大内存
JVMMemory JVM内存
ReservedOsMemory 保留的操作系统内存
ThreadStackSize 线程栈的大小 StackOverflowError:如果在线程执行的过程中,栈空间不够用,那么JVM就会抛出此异常,这种情况一般是死递归造成的。-Xss
垃圾回收GC
定义:虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配
gc触发条件:
1 当应用程序空闲时,即没有应用线程在运行时,GC会被调用
2 Java堆内存不足时,GC会被调用
gc算法
判断垃圾对象
引用计数器算法
定义:引用计数器算法是给每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用失效的时候,计数器-1,当计数器为0的时候,JVM就认为对象不再被使用,是“垃圾”了。
优点:引用计数器实现简单,效率高
缺点:不能解决循环引用问问题,同时每次计数器的增加和减少都带来了很多额外的开销,所以在JDK1.1之后,这个算法已经不再使用了。
根搜索算法
定义:根搜索方法是通过一些“GC Roots”对象作为起点,从这些节点开始往下搜索,搜索通过的路径成为引用链(Reference Chain),当一个对象没有被GC Roots的引用链连接的时候,说明这个对象是不可用的
优点:解决循环引用问题,不影响对象额外开销
GC Root
吗
方法区域中的类静态属性引用的对象
方法区域中常量引用的对象
本地方法栈中JNI(Native方法)的引用的对象
标记 - 清除算法
标记清除算法是最基础的收集算法,其他收集算法都是基于这种思想。标记清除算法分为“标记”和“清除”两个阶段:首先标记出需要回收的对象,标记完成之后统一清除对象。
①.标记和清除过程效率不高 。
②.标记清除之后会产生大量不连续的内存碎片
示意图
示意图
复制算法
复制算法是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上,然后把这块内存整个清理掉
优点:复制算法实现简单,运行效率高。 缺点:每次只能使用其中的一半,造成内存的利用率不高
示意图
复制算法
复制算法
标记—整理算法
在标记阶段,确定所有要回收的对象,并做标记。存活对象往内存的一端移动,然后直接回收边界以外的内存。
缺点:实现复杂,开销大。优点:和复制算法比 节省内存
示意图
标记-整理算法
分代收集算法
根据对象的存活周期的不同将内存划分为几块。一般把java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-整理”算法进行回收。
gc过程
当一个对象被创建时,需要给其分配内存,分配到Eden
当Eden空间足够时,内存申请结束。否则到下一步
JVM试图释放在Eden中所有不活跃的对象(minor collection),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;
Survivor区被用来作为Eden及old的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区;
当old区空间不够时,JVM会在old区进行major collection
完全垃圾收集后,若Survivor及old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现"Out of memory错误";
gc分类
yongGC
条件:eden空间不足
清空Eden+from survivor中所有no ref的对象占用的内存
将eden+from sur中所有存活的对象copy到to sur中
一些对象将晋升到old中:
to sur放不下的
存活次数超过turning threshold中的
重新计算tenuring threshold(serial parallel GC会触发此项)
重新调整Eden 和from的大小(parallel GC会触发此项)
fullGC
条件:old空间不足
perm空间不足
显示调用System.GC, RMI等的定时触发
YGC时的悲观策略
dump live的内存信息时(jmap –dump:live)
清空heap中no ref的对象
permgen中已经被卸载的classloader中加载的class信息
如配置了CollectGenOFirst,则先触发YGC(针对serial GC)
如配置了ScavengeBeforeFullGC,则先触发YGC(针对serial GC)
垃圾收集器
串行收集器-Serial:最基本,历史最悠久的收集器,单线程收集垃圾内存,在新生代采用复制算法,在老生代使用标记-整理算法,虚拟机运行在Client模式时的默认新生代收集器
-XX:+UseSerialGC
stop-the-world
并行收集器:Serial的多线程版本,主要用于新生代收集。与CMS收集器配合成为现在最常用的server收集器
-XX:+UseParallelGC :选择垃圾收集器为并行收集器。 此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:ParallelGCThreads=20 :配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
stop-the-world
并发收集器:并发收集器GC时GC线程和应用线程大部分时间是并发执行,只是在初始标记(initial mark)和二次标记(remark)时需要stop-the-world,这可以大大缩短停顿时间(pause time)
-XX:+UseConcMarkSweepGC :设置年老代为并发收集。
-XX:+UseParNewGC :设置年轻代为并行收集。
-XX:ParallelGCThreads=n :设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
CMS:Concurrent Mark Sweep收集器,大名鼎鼎,其目标是获取最短回收停顿时间,是server模式下最常用的收集器.JDK 1.5中发布的CMS
CMS收集器在年轻代中使用和parallel 收集器相同的算法
gc步骤
初始化标记
(stop-the-world事件) 标记老年代中依然存活的对象,包括那些从年轻代晋升来的,这个过程通常比较短暂(译者注:存活我理解为被程序引用,不可回收的对象)
并发标记 和应用程序并发执行的,扫描整个老年代,是从那些标记的对象,或者间接标记的对象开始扫描。第2、3、5个阶段是并发执行的,在这个过程中,在CMS的代中新分配的对象(包括晋升的对象)会被立即标记为存活状态。
重新标记
(stop-the-world事件) 该阶段发现那些被并发标记错过的对象,因为并发标记是和应用程序并发执行的,在标记线程完成对某个对象的跟踪那刻,应用程序可能对对象进行了更新。
并发清理 收集那些在标记阶段没有标记的对象,消亡对象所占的空间会被添加到释放列表里用于重新分配,对死亡对象的聚集工作就发生在这个点。注意:存活的对象不会被移动。
并发重置 做一些数据结构的清理工作,为下一次收集做准备
G1
java 7 update 9 或者以后的版本,可以使用参数-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC来启用
G1是一个适用于大内存、多CPU情景的垃圾收集器,推荐6G+
目标是在维持高效率回收(high thoughput)的同时,提供软实时中断特性。用户可以指定一个时间上限,如果垃圾回收导致的程序暂停超过了用户设定的时间上限,会打断垃圾回收,恢复程序的执行
特点
并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿的时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。
分代收集:与其他收集器一样,分代概念在G1中依然得以保留。虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
空间整合:与CMS的“标记—清理”算法不同,G1从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC
可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。
数据结构
Remembered Sets(RSets):每个堆区域都有一个RSet,记录该区域里的对象引用。有了RSet,就可以对区域进行并行、独立的收集
Collection Sets(CSets):GC要收集的区域集合,集合里的区域可以是任意角色(Eden、Survivor或老年代)。CSet里记录的区域都会被清空(回收掉或者移走)。
组成
堆被划分为很多区域,区域大小由JVM确定(用户也可以设置),通常是1MB-32MB,大约2000个。
区域会具备“Eden”、“Survivor”、“老年代”的角色,但它们不是连续的。还有另外一种区域,叫Humongous区域,它用来存放大对象(大小超过区域大小的50%),这些区域是连续的。
gc步骤
初始化标记(stop_the_world事件) 这是一个stop_the_world的过程,是随着年轻代GC做的,标记survivor区域(根区域),这些区域可能含有对老年代对象的引用。
根区域扫描 扫描survivor区域中对老年代的引用,这个过程和应用程序一起执行的,这个阶段必须在年轻代GC发生之前完成。
并发标记 查找整个堆中存活的对象,这也是和应用程序一起执行的。这个阶段可以被年轻代的垃圾收集打断。
重新标记(stop-the-world事件) 完成堆内存活对象的标记。使用了一个叫开始前快照snapshot-at-the-beginning (SATB)的算法,这个会比CMS collector使用的算法快。
清理(stop-the-world事件,并且是并发的)
对存活的对象和完全空的区域进行统计(stop-the-world)
刷新Remembered Sets(stop-the-world)
重置空的区域,把他们放到free列表(并发)(译者注:大体意思就是统计下哪些区域又空了,可以拿去重新分配了)
复制(stop-the-world事件) 这个stop-the-world的阶段是来移动和复制存活对象到一个未被使用的区域,这个可以是年轻代区域,打日志的话就标记为 [GC pause (young)]。或者老年代和年轻代都用到了,打日志就会标记为[GC Pause (mixed)]。
内存示意图
示意图
命令
-XX:+UseG1GC——让JVM使用G1垃圾回收器
-XX:MaxGCPauseMillis=200——设置GC暂停时间目标值,缺省200毫秒。但这不是硬指标,JVM会尽力满足
-XX:InitiatingHeapOccupancyPercent=45——整个堆被占用多少之后开始进行GC,缺省为45,0表示持续不停进行GC
-XX:NewRatio=n——年轻代和老年代的比例,缺省为2
-XX:SurvivorRatio=n——Eden和Survivro的比例,缺省为8
-XX:ParallelGCThreads=n --设置垃圾收集器在并行阶段使用的线程数,默认值随JVM运行的平台不同而不同.
-XX:ConcGCThreads=n --并发垃圾收集器使用的线程数量. 默认值随JVM运行的平台而不同
-XX:G1ReservePercent=n——保留的堆大小,减少晋升过程中出错的可能性,也就是增加可用的to-space内存,缺省是10
-XX:G1HeapRegionSize=n——G1中,堆分为大小相等的区域。这个参数设置区域的大小,缺省值取决于堆的总大小,有效取值是1M-32M。
优化建议
不要设置年轻代的大小(-Xmn),否则会扰乱G1的缺省行为,JVM也不会满足用户指定的暂停时间。而且设置了固定值的话,G1将无法随需扩展年轻代的大小
如果GC的晋升过程中遇到堆区域溢出(使用-XX:+PrintGCDetails看到to-space overflow),可以通过下面几种方式避免:
增加-XX:G1ReservePercent=n,缺省值是10。这可以增加可用的to-space内存
使用-XX:ConcGCThreads=n增加标记线程数目
-XX:+ParallelRefProcEnabled
如果应用程序有大量的引用对象或可终结对象要处理,使用该命令行选项可以减少垃圾收集的持续时间
-server -verbose:gc -Xms10240m -Xmx10240 -XX:PermSize=128m -XX:MaxPermSize=128m -Xss256k -Xloggc:${LOG_HOME}/logs/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOG_HOME}/logs/HeapDumpOnOutOfMemoryError.log -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:MaxGCPauseMillis=10 -XX:GCPauseIntervalMillis=200 -XX:+DisableExplicitGC -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
jdk8优化
字符串重复消除技术(String deduplication),之前字符串以及内部的char[]数组大量消耗了内存空间,在新的G1垃圾收集器中,将会对内存中重复的字符串进行优化,使他们指向同一个字符数组,以避免相同的字符串出现而使堆处理效率低下,你可以使用 -XX:+UseStringDeduplicationJVM参数来开启
删除了在堆中为类的元数据、内部字符串和静态变量分配 permgen空间的部分。过去如果加载大量的类到内存中经常会出现内存溢出异常,并且开发人员需要在这个方面做大量的工作,所以如果这段通过JVM来管理了将是一个不错误的优化。
-XX:MaxMetaspaceSize
代码编译和执行
Java源码编译机制
分析和输入到符号表
注解处理
语义分析和生成class文件
组成
结构信息。包括class文件格式版本号及各部分的数量与大小的信息
元数据。对应于Java源码中声明与常量的信息。包含类/继承的超类/实现的接口的声明信息、域与方法声明信息和常量池
方法信息。对应Java源码中语句和表达式对应的信息。包含字节码、异常处理器表、求值栈与局部变量区大小、求值栈的类型记录、调试符号信息
解释执行过程
代码的装入
类加载机制
Bootstrap ClassLoader
负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
Extension ClassLoader
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
App ClassLoader
负责记载classpath中指定的jar包及目录中class
Custom ClassLoader
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader
加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
代码的校验
由字节码校验器进行检查。校验器可发现操作数栈溢出,非法数据类型转化等多种错误。通过校验后,代码便开始执行了
代码的执行
即时编译方式:解释器先将字节码编译成机器码,然后再执行该机器码
解释执行方式:解释器通过每次解释并执行一小段代码来完成Java字节码程 序的所有操作
示意图
分支主题
0 条评论
下一页