JVM学习总结一

1. JVM内存

 

如图所示,JVM主要包括两个子系统和两个组件。两个子系统分别是Class loader子系统和Execution engine(执行引擎) 子系统;两个组件分别是Runtime data area (运行时数据区域)组件和Native interface(本地接口)组件。
 
Class loader子系统的作用:根据给定的全限定名类名(如 java.lang.Object)来装载class文件的内容到 Runtime data area中的method area(方法区域)。Java程序员可以extends java.lang.ClassLoader类来写自己的Class loader。  
Execution engine子系统的作用:执行classes中的指令。任何JVM specification实现(JDK)的核心都是Execution engine,不同的JDK例如Sun 的JDK 和IBM的JDK好坏主要就取决于他们各自实现的Execution engine的好坏。
Native interface组件:与native libraries交互,是其它编程语言交互的接口。当调用native方法的时候,就进入了一个全新的并且不再受虚拟机限制的世界,所以也很容易出现JVM无法控制的native heap OutOfMemory。
Runtime Data Area组件:这就是我们常说的JVM的内存了。它主要分为五个部分——
1、Heap (堆):一个Java虚拟实例中只存在一个堆空间
2、Method Area(方法区域):被装载的class的信息存储在Method area的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件内容并把它传输到虚拟机中。
3、Java Stack(java的栈):虚拟机只会直接对Java stack执行两种操作:以帧为单位的压栈或出栈
4、Program Counter(程序计数器):每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。PC寄存器的内容总是指向下一条将被执行指令的饿地址,这里的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。  
5、Native method stack(本地方法栈):保存native方法进入区域的地址
以上五部分只有Heap 和Method Area是被所有线程的共享使用的;而Java stack, Program counter 和Native method stack是以线程为粒度的,每个线程独自拥有自己的部分。
了解JVM的系统结构,再来看看JVM内存回收问题了——
Sun的JVM Generational Collecting(垃圾回收)原理是这样的:把对象分为年青代(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空间,而持久代则是之前提到的Method Area,不属于Heap。

----------------------------------------------------JVM的内存分区-------------------------------------------

JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method) 

 

堆区:

1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)

2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放数据

栈区:

1.每个线程包含一个栈区,栈中只保存原始类型数据和对象和对象引用(不是对象),对象都存放在堆区中

2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。

3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)

方法区:

1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的classstatic变量。

2.方法区中包含的都是在整个程序中永远唯一的元素,如classstatic变量。

 

-----------------------------------   JVM 运行状态常用指令--------------------------------------------

查看JVM 运行状态时的某些信息常用指令:

2. jstat

这个命令对于查看Jvm的堆栈信息很有用。能够查看eden,survivor,old,permheapcapacity,utility信息

对于查看系统是不是有能存泄漏以及参数设置是否合理有不错的意义

 

----------------------------------

jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ] 

 

vmid 可用jps -ml查看 

 

1.generalOption: 

 

-help Display help message. 

-version Display version information. 

-options Display list of statistics options. See the Output Options section below. 

 

2.outputOptions: 

class  

类加载器行为统计,Statistics on the behavior of the class loader. 

compiler JIT

编译器行为,Statistics of the behavior of the HotSpot Just-in-Time compiler. 

gc heap   

垃圾收集行为,Statistics of the behavior of the garbage collected heap. 

gccapacity 

各代区域容量统计,Statistics of the capacities of the generations and their corresponding spaces. 

gccause 

类似-gcutil,附上垃圾收集的原因。Summary of garbage collection statistics (same as -gcutil), with the cause of the last and current (if applicable) garbage collection events. 

gcnew 

新生代gc行为。Statistics of the behavior of the new generation. 

gcnewcapacity 

新生代容量统计。Statistics of the sizes of the new generations and its corresponding spaces. 

gcold 

老生代gc行为。Statistics of the behavior of the old and permanent generations. 

gcoldcapacity 

老生代容量统计。Statistics of the sizes of the old generation. 

gcpermcapacity 

永久代容量统计。Statistics of the sizes of the permanent generation. 

gcutil 

当前JVM垃圾收集描述。Summary of garbage collection statistics. 

printcompilation 

方法编译统计。HotSpot compilation method statistics.  

 

 

jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ] 

jstat -op -t -h20 vmid ms/m 

 

-t 第一列附加启动以来的timestamp 

-h20 每打印20行时附加列头信息 

vmid  jvm进程id 

ms/m   打印时长间隔 

 

 

 

各选项输出列说明 

-gc Option 

 

examples: 

 

jstat -gc -h50 29617 3000 

每隔3000ms输出一次gc统计信息,且每隔50行输出一次列头信息 

S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT   

9536.0 9152.0 544.0   0.0   68672.0  66932.0   174784.0   88186.1   53120.0 51319.9     36    0.902   1      0.139    1.041 

9536.0 9152.0 544.0   0.0   68672.0  66932.0   174784.0   88186.1   53120.0 51319.9     36    0.902   1      0.139    1.041 

 

Garbage-collected heap statistics  Column Description 

 

S0C  Current survivor space 0 capacity (KB). 当前Survivor 0容量  S0C

S1C  Current survivor space 1 capacity (KB). 当前Survivor 1容量  S1C

S0U  Survivor space 0 utilization (KB). 当前Survivor 0占用       S0U

S1U  Survivor space 1 utilization (KB). 当前Survivor 1占用       S1U

EC  Current eden space capacity (KB). 当前eden容量               EC

EU  Eden space utilization (KB). 当前eden占用   EU

OC  Current old space capacity (KB). 当前老区容量   OC

OU  Old space utilization (KB). 当前老区占用   OU

PC  Current permanent space capacity (KB). 当前永久代容量   PC

PU  Permanent space utilization (KB). 当前永久代占用   PU

YGC  Number of young generation GC Events. 年轻代GC累计次数      YGC

YGCT  Young generation garbage collection time. 年轻代GC耗时     YGCT

FGC  Number of full GC events. Full GC 累计次数    FGC

FGCT  Full garbage collection time. Full GC 累计耗时   FGCT

GCT  Total garbage collection time.  总GC累计耗时   GCT

 

总堆:262144k 

新生代:98304K 

幸存012288k 

幸存112288k 

eden:73728k 

老生代:163840k 

永久代初始:51200K 

永久代最大:65536K

 

PermGen space简介

PermGen space的全称是Permanent Generation space,是指内存的永久保存区域OutOfMemoryError: PermGen space从表面上看就是内存溢出,解决方法也一定是加大内存。

说说为什么会内存益出:

1)这一部分用于存放ClassMeta的信息,Class在被 Load的时候被放入PermGen space区域,它和和存放InstanceHeap区域不同。

2GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APPLOAD很多CLASS的话,就很可能出现PermGen space错误。这种错误常见在web服务器对JSP进行pre compile的时候。

如果你的WEB APP下都用了大量的第三方jar,其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。

解决方法: 手动设置MaxPermSize大小

修改TOMCAT_HOME/bin/catalina.sh,在echo "Using CATALINA_BASE:   $CATALINA_BASE"上面加入以下行:

       JAVA_OPTS="-server -XX:PermSize=64M -XX:MaxPermSize=128m

建议:将相同的第三方jar文件移置到tomcat/shared/lib目录下,这样可以减少jar 文档重复占用内存。

OutOfMemory:PermGen Space异常的处理和分析

Java程序员没有遇到过OutOfMemory简直就是不可能的事情!

可见在Java的世界中,太多的不确定因素导致Java运行程序直接崩溃,直接抛出OutOfMemory异常,而一旦遇到了这个问题,调查起来就非常的困难。在JDK 5.0以前,OutOfMemory只有这么一句话: java.lang.OutOfMemory Exception…基本上无从下手,无从分析。从JDK 5.0以后对OutOfMemory增加了许多的详细说明,为这个异常的分析提供了很大的便利。

这次遇到的问题就是会抛出OutOfMemory:PermGen Space的异常,这个异常非常有意思,根据【此文章】的描述,这是一个Sun JVMbug,从2003年开始,一只到现在都没有解决。而且提出来的解决方案是使用JRockitBug产生的原因已经找到,就是因为JVM在分配PermGen Space的时候出现了PermGen Space不足的情况,默认情况下 PermGen的大小为64M,在不换用JRockit的情况下,可以在启动JVM的时候添加一个参数: -XX: MaxPermSize= 128m| 256m| 512m

那么究竟什么是PermGen呢?

PermGen 原来是指Permanent Generation,本身是在Java的垃圾收集机制(GC)中产生的一个概念。Java的垃圾收集机制最早只是遍历所有的对象,如果发现某个对象没有被引用,则回收,这是在早期的Java 1.0Java 1.1的时候的GC规则。慢慢的,这样一种愚蠢的”GC算法成为了JVM性能的瓶颈,在拥有大量数据的Java应用程序中,GC的算法被高度强化,于是各种各样高效的JVM GC算法被发展了起来。从J2SE也就是Java 1.2开始,JVM引入了多种GC算法,其中一种用的非常多的就是Generational Collection,中文也叫做分代收集法”。

分代收集法摈弃了对所有对象的遍历,而是采用一些经验属性去避免额外的工作(While naive garbage collection examines every live object in the heap, generational collection exploits several empirically observed properties of most applications to avoid extra work)。其中导入了一个非常关键的概念:infant mortality (幼儿死亡率),这表示越是新生成的变量或者对象,越容易被收集。下面一张图表示了对象的生命周期,横轴表示的是测试到对象的生命周期,纵轴表示在一个指定的生命周期上被回收的对象数量。

 

可以看到,在使用了分代收集法以后,年轻一代的对象被收集的比例最高。并且在内存中的对象会按照不同的年龄来划分,当一个年龄段的对象满了以后,在这个年龄段上就会发生垃圾收集,从最年轻的一代开始,一直到永生代,在内存中,所有的对象可以划分为很多代,最后的一代“永生代就是“Permanent Generation”,这里就是直接引出“Permanent Generation”概念的地方。具体可以参考下图:

 

根据前面所说的情况,在分代垃圾收集的情况下会产生Permanent Generation的概念,而这个分代垃圾收集法是并行收集和并发收集的基础,所以Permanent Generation会一直存在,那么这个Permanent Generation究竟是做什么用的呢?这里保存了JVM中所有对象的类信息,包括类的元数据,还有方法描述等等,所以这一代内存垃圾收集算法是不一样的,在Java大程序的情况下,尤其是J2EE 或者说Java EE的大型应用程序上,Permanent Generation的大小会直接限定能载入类的数量和大小。

【解决办法】就是设定JVM启动的时候参数,可以如下设置:

java -XX: PermSize=64m -XX: MaxPermSize=128m

另外PermSize MaxPermSize如果设置为相同还可以在一定程度上提高性能,因为,PermSize在不断的变化中会需要转移其中的数据。如果固定了以后,则可以减少每次扩大PermSize带来的性能损失。

 

-------------------

3. jstack

这个是用来查看jvm当前的thread dump的。可以看到当前Jvm里面的线程状况。

这个对于查找blocked线程比较有意义

 

jstack - Stack Trace 

为指定的线程输出 java 的线程堆栈信息,包括了进程里的所有线程。每一个线程 frame ,包括类全名,方法名,代码行。 

 

java.lang.Thread.State : RUNNABLE BLOCKED TIMED_WATTING(sleep 后会进入这种状态 (如果是 BLOCKED 状态就要注意了,看看 blocked 的状态在等待什么?因为什么而阻塞?)最常见的情况是线程在等待网络的读写,比如当网络数据没有准备好读时,线程处于这种等待状态,而一旦有数据准备好读之后,线程会重新激活,读取并处理数据。 

 

 

在线程中,有一些 JVM 内部的后台线程,来执行譬如垃圾回收,或者低内存的检测等等任务,这些线程往往在 JVM 初始化的时候就存在,如下所示: 

"Low Memory Detector" daemon prio=10 tid=0x081465f8 nid=0x7 runnable [0x00000000..0x00000000] 

"CompilerThread0" daemon prio=10 tid=0x08143c58 nid=0x6 waiting on condition [0x00000000..0xfb5fd798] 

"Signal Dispatcher" daemon prio=10 tid=0x08142f08 nid=0x5 waiting on condition [0x00000000..0x00000000] 

"Finalizer" daemon prio=10 tid=0x08137ca0 nid=0x4 in Object.wait() [0xfbeed000..0xfbeeddb8] 

 

jstack pid

-------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值