`
高成锋
  • 浏览: 52741 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

JVM学习总结一

    博客分类:
  • java
阅读更多

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的内存分区-------------------------------------------

JAVAJVM的内存可分为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 

eden73728k 

老生代: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 文档重复占用内存。

OutOfMemoryPermGen 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

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

分享到:
评论

相关推荐

    jvm自己学习总结

    jvm自己学习总结,对JVM的工作原理进行记录学习笔记

    JVM学习资料+笔记

    1. 参数调整:通过设置JVM启动参数,如-Xms、-Xmx控制堆大小,-XX:+UseG1GC选择垃圾收集器,-XX:MaxHeapFreeRatio、-XX:MinHeapFreeRatio设定内存利用率等。 2. 内存调优:根据应用特点调整新生代、老年代的比例,...

    JVM调优总结.pdf

    JVM调优是一个复杂的过程,它涉及到对Java虚拟机内部工作原理的深刻理解。本文档总结了JVM调优的基础知识和一些核心概念,旨在帮助开发者更好地掌握Java程序的性能优化。 首先,文档提到了Java中的数据类型分为基本...

    JVM调优总结.doc

    "JVM调优总结" JVM调优是一种非常重要的技术,能够帮助我们提高Java应用程序的性能和稳定性。在这篇文章中,我们将...只有通过不断的学习和实践,我们才能更好地掌握JVM调优技术,提高Java应用程序的性能和稳定性。

    JVM调优总结.rar

    【JVM调优总结】 Java虚拟机(JVM)是Java程序运行的基础,它负责解析字节码并将其转换为机器可执行的指令。JVM调优是优化Java应用程序性能的关键步骤,尤其对于大型...不断学习和实践,才能真正驾驭JVM调优的艺术。

    个人总结之—JVM性能调优实战

    本总结旨在分享作者在实践中不断探索与总结的经验,为读者提供一份全面而实用的JVM调优指南。 #### 关键知识点 ##### 1. JVM基础知识 - **JVM结构与工作原理**:了解JVM的基本组成及其工作流程对于进行有效的性能...

    jvm瓶颈定位 java jvm 学习

    Java虚拟机(JVM)是Java开发中的核心组成部分,它负责执行字节码,管理内存,以及优化程序性能。在Java应用的运行过程中,如果出现性能...通过持续学习和实践,开发者能够更好地驾驭JVM,解决实际开发中的性能问题。

    java与jvm知识总结

    1. **堆**:所有对象都在堆中创建,是JVM中最大的内存区域。垃圾收集器主要在这里工作,负责对象的分配和回收。 2. **方法区**:存储类信息、常量、静态变量等数据。在Java 8之后,被元空间(Metaspace)取代,元...

    jvm的基础知识总结

    JVM(Java虚拟机)是运行Java程序的核心,它负责解释执行Java字节码。...随着Java版本的更新,JVM也不断地引入新的特性和优化,学习和掌握JVM的基础知识对于Java开发者来说是一项基础且重要的任务。

    Java虚拟机JVM性能优化(一):JVM知识总结

    本篇文章将对JVM的基础知识进行总结,并探讨如何通过调整JVM参数来提升性能。 首先,我们需要了解JVM的主要组成部分:类装载器、运行时数据区、执行引擎、本地方法接口和本地方法库。其中,类装载器负责加载、验证...

    JVM调优总结

    1. **内存配置**:JVM内存分为堆内存(Heap)、方法区(Method Area)、栈内存(Stack)、程序计数器(PC Register)和本地方法栈(Native Method Stack)。调优时需关注-Xms、-Xmx设置堆内存大小,-XX:PermSize和-...

    JVM调优world总结

    - 学习更多关于JVM、Java性能调优的书籍和在线资源,如《Java性能优化权威指南》等。 总之,JVM调优是一个系统工程,需要从多个维度进行深入理解和实践,才能达到理想的优化效果。同时,持续关注Java和JVM的新特性...

    JVM调优总结与ava虚拟机:JVM高级特性与最佳实践(最新第二版)

    《JVM调优总结》与《Java虚拟机:JVM高级特性与最佳实践》是两本深入探讨Java虚拟机(JVM)的书籍,对于Java开发者来说,它们提供了丰富的知识和实践经验,尤其对于想要理解JVM工作原理以及进行性能优化的专业人士更...

    jvmjava,java实现的JVM。.zip

    《Java实现的JVM——深入...总结,"jvmjava"项目是一个极好的学习资源,它使开发者有机会亲手构建一个JVM,从底层理解Java应用程序的运行机制。无论是初学者还是经验丰富的开发者,都能从中受益,提升自己的技术水平。

    JVM学习笔记核心知识点整理

    ### JVM学习笔记核心知识点整理 #### 一、引言与背景 随着软件开发技术的不断发展,Java作为一种广泛应用的编程语言,其背后的核心技术——Java虚拟机(JVM)的重要性日益凸显。掌握JVM不仅可以帮助开发者更好地理解...

    JVM

    Java虚拟机(JVM)是Java编程语言的核心组成部分,它为Java程序提供了运行环境,使得Java代码能够在不同的操作系统上“一次编写,...通过学习JVM源码,我们可以更深入地了解其内部细节,从而成为更出色的Java程序员。

    JVM入门实战/arthas实战/垃圾回收算法/垃圾回收器/jvm内存模型分析

    第一节:学习JVM的意义和目标 1.1 意义: 1.2 目标: 第二节:JVM内存模型 1.1 概念 1.2 JVM内存模型 1.3 Heap堆内存模型 第三节:定位垃圾对象的依据 1.1 引用计数法 1.2 可达性算法 第四节:垃圾回收算法 ...

Global site tag (gtag.js) - Google Analytics