JVM学习总结.docx
- 文档编号:5809025
- 上传时间:2023-01-01
- 格式:DOCX
- 页数:12
- 大小:142.93KB
JVM学习总结.docx
《JVM学习总结.docx》由会员分享,可在线阅读,更多相关《JVM学习总结.docx(12页珍藏版)》请在冰豆网上搜索。
JVM学习总结
1.JVM内存
如图所示,JVM主要包括两个子系统和两个组件。
两个子系统分别是Classloader子系统和Executionengine(执行引擎)子系统;两个组件分别是Runtimedataarea(运行时数据区域)组件和Nativeinterface(本地接口)组件。
Classloader子系统的作用:
根据给定的全限定名类名(如java.lang.Object)来装载class文件的内容到Runtimedataarea中的methodarea(方法区域)。
Java程序员可以extendsjava.lang.ClassLoader类来写自己的Classloader。
Executionengine子系统的作用:
执行classes中的指令。
任何JVMspecification实现(JDK)的核心都是Executionengine,不同的JDK例如Sun的JDK和IBM的JDK好坏主要就取决于他们各自实现的Executionengine的好坏。
Nativeinterface组件:
与nativelibraries交互,是其它编程语言交互的接口。
当调用native方法的时候,就进入了一个全新的并且不再受虚拟机限制的世界,所以也很容易出现JVM无法控制的nativeheapOutOfMemory。
RuntimeDataArea组件:
这就是我们常说的JVM的内存了。
它主要分为五个部分——
1、Heap(堆):
一个Java虚拟实例中只存在一个堆空间
2、MethodArea(方法区域):
被装载的class的信息存储在Methodarea的内存中。
当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件内容并把它传输到虚拟机中。
3、JavaStack(java的栈):
虚拟机只会直接对Javastack执行两种操作:
以帧为单位的压栈或出栈
4、ProgramCounter(程序计数器):
每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。
PC寄存器的内容总是指向下一条将被执行指令的饿地址,这里的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。
5、Nativemethodstack(本地方法栈):
保存native方法进入区域的地址
以上五部分只有Heap和MethodArea是被所有线程的共享使用的;而Javastack,Programcounter和Nativemethodstack是以线程为粒度的,每个线程独自拥有自己的部分。
了解JVM的系统结构,再来看看JVM内存回收问题了——
Sun的JVMGenerationalCollecting(垃圾回收)原理是这样的:
把对象分为年青代(Young)、年老代(Tenured)、持久代(Perm),对不同生命周期的对象使用不同的算法。
(基于对对象生命周期分析)
如上图所示,为Java堆中的各代分布。
1.Young(年轻代)
年轻代分三个区。
一个Eden区,两个Survivor区。
大部分对象在Eden区中生成。
当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制年老区(Tenured。
需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。
而且,Survivor区总有一个是空的。
2.Tenured(年老代)
年老代存放从年轻代存活的对象。
一般来说年老代存放的都是生命期较长的对象。
3.Perm(持久代)
用于存放静态文件,如今Java类、方法等。
持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。
持久代大小通过-XX:
MaxPermSize=进行设置。
举个例子:
当在程序中生成对象时,正常对象会在年轻代中分配空间,如果是过大的对象也可能会直接在年老代生成(据观测在运行某程序时候每次会生成一个十兆的空间用收发消息,这部分内存就会直接在年老代分配)。
年轻代在空间被分配完的时候就会发起内存回收,大部分内存会被回收,一部分幸存的内存会被拷贝至Survivor的from区,经过多次回收以后如果from区内存也分配完毕,就会也发生内存回收然后将剩余的对象拷贝至to区。
等到to区也满的时候,就会再次发生内存回收然后把幸存的对象拷贝至年老区。
通常我们说的JVM内存回收总是在指堆内存回收,确实只有堆中的内容是动态申请分配的,所以以上对象的年轻代和年老代都是指的JVM的Heap空间,而持久代则是之前提到的MethodArea,不属于Heap。
----------------------------------------------------JVM的内存分区-------------------------------------------
JAVA的JVM的内存可分为3个区:
堆(heap)、栈(stack)和方法区(method)
堆区:
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。
(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放数据
栈区:
1.每个线程包含一个栈区,栈中只保存原始类型数据和对象和对象引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:
基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区:
1.又叫静态区,跟堆一样,被所有的线程共享。
方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
-----------------------------------JVM运行状态常用指令--------------------------------------------
查看JVM运行状态时的某些信息常用指令:
2.jstat
这个命令对于查看Jvm的堆栈信息很有用。
能够查看eden,survivor,old,perm等heap的capacity,utility信息
对于查看系统是不是有能存泄漏以及参数设置是否合理有不错的意义
----------------------------------
jstat[generalOption|outputOptionsvmid[interval[s|ms][count]]]
vmid可用jps-ml查看
1.generalOption:
-helpDisplayhelpmessage.
-versionDisplayversioninformation.
-optionsDisplaylistofstatisticsoptions.SeetheOutputOptionssectionbelow.
2.outputOptions:
class
类加载器行为统计,Statisticsonthebehavioroftheclassloader.
compilerJIT
编译器行为,StatisticsofthebehavioroftheHotSpotJust-in-Timecompiler.
gcheap
垃圾收集行为,Statisticsofthebehaviorofthegarbagecollectedheap.
gccapacity
各代区域容量统计,Statisticsofthecapacitiesofthegenerationsandtheircorrespondingspaces.
gccause
类似-gcutil,附上垃圾收集的原因。
Summaryofgarbagecollectionstatistics(sameas-gcutil),withthecauseofthelastandcurrent(ifapplicable)garbagecollectionevents.
gcnew
新生代gc行为。
Statisticsofthebehaviorofthenewgeneration.
gcnewcapacity
新生代容量统计。
Statisticsofthesizesofthenewgenerationsanditscorrespondingspaces.
gcold
老生代gc行为。
Statisticsofthebehavioroftheoldandpermanentgenerations.
gcoldcapacity
老生代容量统计。
Statisticsofthesizesoftheoldgeneration.
gcpermcapacity
永久代容量统计。
Statisticsofthesizesofthepermanentgeneration.
gcutil
当前JVM垃圾收集描述。
Summaryofgarbagecollectionstatistics.
printcompilation
方法编译统计。
HotSpotcompilationmethodstatistics.
jstat[generalOption|outputOptionsvmid[interval[s|ms][count]]]
jstat-op-t-h20vmidms/m
-t第一列附加启动以来的timestamp
-h20每打印20行时附加列头信息
vmidjvm进程id
ms/m打印时长间隔
各选项输出列说明
-gcOption
examples:
jstat-gc-h50296173000
每隔3000ms输出一次gc统计信息,且每隔50行输出一次列头信息
S0CS1CS0US1UECEUOCOUPCPUYGCYGCTFGCFGCTGCT
9536.09152.0544.00.068672.066932.0174784.088186.153120.051319.9360.90210.1391.041
9536.09152.0544.00.068672.066932.0174784.088186.153120.051319.9360.90210.1391.041
Garbage-collectedheapstatisticsColumnDescription
S0CCurrentsurvivorspace0capacity(KB).当前Survivor0容量S0C
S1CCurrentsurvivorspace1capacity(KB).当前Survivor1容量S1C
S0USurvivorspace0utilization(KB).当前Survivor0占用S0U
S1USurvivorspace1utilization(KB).当前Survivor1占用S1U
ECCurrentedenspacecapacity(KB).当前eden容量EC
EUEdenspaceutilization(KB).当前eden占用EU
OCCurrentoldspacecapacity(KB).当前老区容量OC
OUOldspaceutilization(KB).当前老区占用OU
PCCurrentpermanentspacecapacity(KB).当前永久代容量PC
PUPermanentspaceutilization(KB).当前永久代占用PU
YGCNumberofyounggenerationGCEvents.年轻代GC累计次数YGC
YGCTYounggenerationgarbagecollectiontime.年轻代GC耗时YGCT
FGCNumberoffullGCevents.FullGC累计次数FGC
FGCTFullgarbagecollectiontime.FullGC累计耗时FGCT
GCTTotalgarbagecollectiontime.总GC累计耗时GCT
总堆:
262144k
新生代:
98304K
幸存0:
12288k
幸存1:
12288k
eden:
73728k
老生代:
163840k
永久代初始:
51200K
永久代最大:
65536K
PermGenspace简介
PermGenspace的全称是PermanentGenerationspace,是指内存的永久保存区域OutOfMemoryError:
PermGenspace从表面上看就是内存溢出,解决方法也一定是加大内存。
说说为什么会内存益出:
(1)这一部分用于存放Class和Meta的信息,Class在被Load的时候被放入PermGenspace区域,它和和存放Instance的Heap区域不同。
(2)GC(GarbageCollection)不会在主程序运行期对PermGenspace进行清理,所以如果你的APP会LOAD很多CLASS的话,就很可能出现PermGenspace错误。
这种错误常见在web服务器对JSP进行precompile的时候。
如果你的WEBAPP下都用了大量的第三方jar,其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。
解决方法:
手动设置MaxPermSize大小
修改TOMCAT_HOME/bin/catalina.sh,在echo"UsingCATALINA_BASE:
$CATALINA_BASE"上面加入以下行:
JAVA_OPTS="-server-XX:
PermSize=64M-XX:
MaxPermSize=128m
建议:
将相同的第三方jar文件移置到tomcat/shared/lib目录下,这样可以减少jar文档重复占用内存。
OutOfMemory:
PermGenSpace异常的处理和分析
Java程序员没有遇到过OutOfMemory简直就是不可能的事情!
可见在Java的世界中,太多的不确定因素导致Java运行程序直接崩溃,直接抛出OutOfMemory异常,而一旦遇到了这个问题,调查起来就非常的困难。
在JDK5.0以前,OutOfMemory只有这么一句话:
java.lang.OutOfMemoryException…基本上无从下手,无从分析。
从JDK5.0以后对OutOfMemory增加了许多的详细说明,为这个异常的分析提供了很大的便利。
这次遇到的问题就是会抛出OutOfMemory:
PermGenSpace的异常,这个异常非常有意思,根据【此文章】的描述,这是一个SunJVM的bug,从2003年开始,一只到现在都没有解决。
而且提出来的解决方案是使用JRockit。
Bug产生的原因已经找到,就是因为JVM在分配PermGenSpace的时候出现了PermGenSpace不足的情况,默认情况下PermGen的大小为64M,在不换用JRockit的情况下,可以在启动JVM的时候添加一个参数:
-XX:
MaxPermSize=128m|256m|512m。
那么究竟什么是PermGen呢?
PermGen原来是指PermanentGeneration,本身是在Java的垃圾收集机制(GC)中产生的一个概念。
Java的垃圾收集机制最早只是遍历所有的对象,如果发现某个对象没有被引用,则回收,这是在早期的Java1.0和Java1.1的时候的GC规则。
慢慢的,这样一种“愚蠢的”GC算法成为了JVM性能的瓶颈,在拥有大量数据的Java应用程序中,GC的算法被高度强化,于是各种各样高效的JVMGC算法被发展了起来。
从J2SE也就是Java1.2开始,JVM引入了多种GC算法,其中一种用的非常多的就是GenerationalCollection,中文也叫做“分代收集法”。
分代收集法摈弃了对所有对象的遍历,而是采用一些经验属性去避免额外的工作(Whilenaivegarbagecollectionexamineseveryliveobjectintheheap,generationalcollectionexploitsseveralempiricallyobservedpropertiesofmostapplicationstoavoidextrawork)。
其中导入了一个非常关键的概念:
infantmortality(幼儿死亡率),这表示越是新生成的变量或者对象,越容易被收集。
下面一张图表示了对象的生命周期,横轴表示的是测试到对象的生命周期,纵轴表示在一个指定的生命周期上被回收的对象数量。
可以看到,在使用了分代收集法以后,年轻一代的对象被收集的比例最高。
并且在内存中的对象会按照不同的“年龄”来划分,当一个年龄段的对象满了以后,在这个年龄段上就会发生垃圾收集,从最年轻的一代开始,一直到“永生代”,在内存中,所有的对象可以划分为很多代,最后的一代“永生代”就是“PermanentGeneration”,这里就是直接引出“PermanentGeneration”概念的地方。
具体可以参考下图:
根据前面所说的情况,在分代垃圾收集的情况下会产生PermanentGeneration的概念,而这个分代垃圾收集法是并行收集和并发收集的基础,所以PermanentGeneration会一直存在,那么这个PermanentGeneration究竟是做什么用的呢?
这里保存了JVM中所有对象的类信息,包括类的元数据,还有方法描述等等,所以这一代内存垃圾收集算法是不一样的,在Java大程序的情况下,尤其是J2EE或者说JavaEE的大型应用程序上,PermanentGeneration的大小会直接限定能载入类的数量和大小。
【解决办法】就是设定JVM启动的时候参数,可以如下设置:
java-XX:
PermSize=64m-XX:
MaxPermSize=128m
另外PermSize和MaxPermSize如果设置为相同还可以在一定程度上提高性能,因为,PermSize在不断的变化中会需要转移其中的数据。
如果固定了以后,则可以减少每次扩大PermSize带来的性能损失。
-------------------
3.jstack
这个是用来查看jvm当前的threaddump的。
可以看到当前Jvm里面的线程状况。
这个对于查找blocked线程比较有意义
jstack-StackTrace
为指定的线程输出java的线程堆栈信息,包括了进程里的所有线程。
每一个线程frame,包括类全名,方法名,代码行。
java.lang.Thread.State:
RUNNABLEBLOCKEDTIMED_WATTING(sleep后会进入这种状态)(如果是BLOCKED状态就要注意了,看看blocked的状态在等待什么?
因为什么而阻塞?
)最常见的情况是线程在等待网络的读写,比如当网络数据没有准备好读时,线程处于这种等待状态,而一旦有数据准备好读之后,线程会重新激活,读取并处理数据。
在线程中,有一些JVM内部的后台线程,来执行譬如垃圾回收,或者低内存的检测等等任务,这些线程往往在JVM初始化的时候就存在,如下所示:
"LowMemoryDetector"daemonprio=10tid=0x081465f8nid=0x7runnable[0x00000000..0x00000000]
"CompilerThread0"daemonprio=10tid=0x08143c58nid=0x6waitingoncondition[0x00000000..0xfb5fd798]
"SignalDispatcher"daemonprio=10tid=0x08142f08nid=0x5waitingoncondition[0x00000000..0x00000000]
"Finalizer"daemonprio=10tid=0x08137ca0nid=0x4inObject.wait()[0xfbeed000..0xfbeeddb8]
jstackpid
-------------------
4.jmap
这个是用来查看jvm当前的heapdump的。
可以看出当前jvm中各种对象的数量,所占空间等等。
尤其值得一提的是这个命令可以到处一份binaryheapdump的bin文件,这个文件能够直接用
EclipseMemoryAnayliser来分析,并找出潜在的内存泄漏的地方。
描述:
输出给定java进程所有的内存使用信息。
使用:
jmap[option]pid
jmap[option]executablecore
jmap[option][server-id@]remote-hostname-or-IP
参数如下:
-heap:
打印jvmheap的情况
-histo:
打印jvmheap的直方图。
其输出信息包括类名,对象数量,对象占用大小。
-histo:
live:
同上,但是只答应存活对象的情况
-permstat:
打印permanentgenerationheap情况
没有可选项时,会输出所有共享的对象映射。
开始地址,映射大小,全路径。
直接jmappid得到如下结果:
0x08048000 46K /work/ProgramFiles/jdk1.6.0_14/bin/java
0xb6763000 74K /work/ProgramFiles/jdk1.6.0_14/jre/lib/i386/libzip.so
0xb6774000 41K /lib/tls/i686/cmov/libnss_files-2.9.so
0xb6780000 37K /lib/tls/i686/cmov/libnss_ni
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- JVM 学习 总结
![提示](https://static.bdocx.com/images/bang_tan.gif)