ref:http://www.cnblogs.com/ityouknow/p/6482464.html

注1:看了大神:纯洁的微笑的JVM系列篇,发现好多地方还是似懂非懂,理解的并不透彻,jvm的调优部分更是稀里糊涂;

    本片主要整理下jvm部分的知识点,方便以后的面试使用,(权且死记硬背了  !>_<!)

注2:由于不同的JVM厂商不同,关于JVM的分区,存在几个不确定的地方,以下简述对这些不甚明了的地方的个人理解,难免有误:

  1、关于JVM的内存划分,“方法区”的叫法是JVM的规范中的术语,而Oracle公司的HotSpot虚拟机对方法区的实现是永久代(PerGen space),所以只有HotSpot才有永久带,也就有了对永久带的配置参数-XX:PermSize,网上的JVM的分析大多对于HotSpot的,但要明白“方法区”与“PerGen space”两者不是等价的。

  2、JDK8中废弃了永久带(PermGen),启用了元空间(Metaspace)

     ref:https://www.cnblogs.com/yulei126/p/6777323.html

    ref:http://www.cnblogs.com/paddix/p/5309550.html

  废弃原因:

  • PermGen现实中经常爆OutOfMemoryError异常,hotspot中有参数实现PerGen空间的配置
  • oracle公司融合自身HotSpot JVM与 JRockit VM两种虚拟机,后者没有永久带,不需要配置;
  • (永久代会为 GC 带来不必要的复杂度,并且回收效率偏低)

  元空间Metaspace也是对方法区的实现,作用同以前的PermGen,其最大不同在于:Metaspace不在JVM中,而是使用本地内存(即JVM以外的本机内存)存放。因此,默认情况下Metaspace只收到本机内存的限制,同样也可以通过参数指定如:-XX:MetaspaceSize等。

  JDK1.7中,就已经将PermGen中的部分数据转移到其他地方存储:如类的静态变量转移到Heap区,字符串也在Heap区???

<---------------------------------------------------------------------正片开始----------------------------------------------->

jvm的体系梳理                               

  • 类的加载机制
  • JVM的内存结构
  • GC算法 垃圾回收
  • GC分析 命令调优

  注:底部给出思维导图参考

1、类的加载机制

  1.1:什么是类的加载:

    (1)将类的.class文件以二进制读入到内存,放入运行时数据区的方法区,然后在堆区创建一个java.lang.Class对象,以封装类在方法区的数据结构。

    (2)类加载的最终产品是位于堆区的Class对象,该Class对象封装类在方法区的数据结构,并向JAVA程序员提供了访问方法区内数据解耦的接口。

  1.2:类的生命周期:

####: 类的生命周期:

  #---- 类的加载

    |---- 装载(Loading)(为了区分加载,这里称其为装载):查找并导入类的Class文件,在堆区创建java.lang.Class的对象。

    |---- 连接过程分为三块:

      |---- 验证:文件的格式、元数据、字节码、符号引用验证,确保被装载的类的正确性,该阶段虽然重要但不是必须的;

      |---- 准备:为类的静态变量(static)分配内存,并将其初始化为默认值;如  public static int value=3 ,此时value的值是0而不是3;而对于:  public static final int value=3  ,该阶段value的值是3,而不是0,可以认为final static 常量在javac的编译期间,为value生成了ConstantValue属性,准备阶段jvm根据ConstantValue的值为value指定值。

      |---- 解析:将符号引用转换为直接引用;

     |---- 初始化:为类的静态变量,静态代码块等赋予正确的初始值。

   #---- 类的使用:new出对象供程序使用。

   #---- 类的卸载:执行垃圾回收。

    补充问题:

    1.2.1:JVM的初始化步骤(初始化过程):

    1. 假如这个类还没有被加载和连接,则程序先加载并连接该类; 
    2. 假如这个类的父类还没有被初始化,则先初始化其直接父类;
    3. 假如类中有初始化语句,则系统依次执行这些初始化语句。 

     1.2.2类的初始化时机:

    1. new创建类的实例时;
    2. 访问某个类或接口的静态变量,或者对该静态变量赋值;
    3. 调用类的静态方法;
    4. 反射(Class.forName("com.wht.Test"));
    5. 初始化某个类的子类时,父类也会进行初始化;
    6. JVM启动时被标明为启动类的类 

     1.2.3:哪几种情况下,JVM会结束生命周期: 

      1、执行了system.exit();

      2、程序正常结束;

      3、执行过程中发生异常或错误而终止;

      4、操作系统的错误导致JVM的进程终止;

  1.3:类加载器

    

    

  • 启动类加载器:Bootstrap ClassLoader,负责加载JDK\jre\lib目录下或被-Xbootclasspath指定位置处,可悲jvm识别的类库;
  • 扩展类加载器:Extension ClassLoader,负责加载JDK\jre\lib\ext目录下或由java.ext.dirs系统变量所指定路径中的所有类库;开发者可以直接使用该加载器;
  • 应用程序加载器:ApplicationClassLoader,负责加载用户类路径(Classpath)所指定的类,开发者可以直接使用该类加载器;

  类的加载机制:

  • 全盘负责:某个类加载器负责加载某个Class,对该Class所依赖和引用的其他Class都由该加载器负责加载,除非显示指定。  
  • 父类委托(双亲委托机制):某个类加载器收到Class加载请求时,现将该请求转至父类加载器,对Class进行加载,父类找不到该类无法完成加载,才尝试自己加载。
  • 缓存机制:所有加载过的Class都会被缓存,当程序中需要使用某个类时,类加载器先从缓存区寻找该Class,不存在,才会读取Class的二进制,生成Class对象存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效。

2、JVM内存结构                                     

    

方法区和堆是所有线程共享的内存区域;而java栈、本地方法栈和程序计数器是运行是线程私有的内存区域

 

  • 堆Heap:存放实例对象,是JVM内存中最大的一块,也是GC的主要区域。

      • 年轻代(8:1:1)

        • Eden空间
        • From Survivor空间
        • To Survivor空间  
      • 老年代 
  • 方法区Method Area:存放被jvm加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  • 程序计数器(Program Counter Register):是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
  • 栈:
      • JVM栈:描述方法执行的内存模型,每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
      • 本地方法栈(Native Method Stacks):同JVM栈,只不过它是为JVM的Native方法服务;

对象分配规则:

  注:相关Minor GC、Major GC&Full GC参见:

    ref1:http://www.importnew.com/15820.html :这是翻译的外国人的总结,感觉有些生硬;

    ref2 :  http://www.cnblogs.com/hnrainll/p/3410042.html :来自博客园,解释的很好,简单明了;

  说明:

  1. 对象优先分配在Eden区,Eden区是连续的内存空间,如果Eden去没有足够的空间,JVM进行一次minor GC或Young GC(即从年轻代回收内存),将剩余的对象复制到一个Survivor区
  2. 大对象直接进入老年代(需要大量连续内存空间的对象),以避免在Eden和两个survivor区间的大量内存拷贝。(新生代采用复制算法收集内存)
  3. 长期存活的对象进入老年代。JVM为每一个对象定义了一个年龄计数器,如果经过1次minor GC后,则进入survivor区,之后每一次Minor GC,该对象的年龄加1,直到达到阈值进老年区。
  4. 动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,那年龄大于或等于该年龄的对象进入老年区。
  5. 空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区剩余值的大小,则进行一次Full GC;如果小于检查HandlePromotionFailure设置,如果出现true则只进行Monitor GC,如果false,则进行Full GC。

   注:另外网上还有另一种回答:

  1. Survivor切换:每次的Minor GC都会将剩余对象添加到该Survivor区,当该Survivor满了以后,将其中活着的复制进另一个Survivor区,以后的Minor GC将或者的对象添加到切换后的Survivor区,两个Survivor总有一个是空的
  2. 进入老年代:当在两个Survivor间切换了几次(HotSpot默认15次),将其中仍然存活的对象,复制到老年代。
  3. Full GC:老年代空间一般较大,当老年代内存不足时,出发Major GC.(Full GC与Major GC的区别网上不清楚,参照ref1

各个内存区域的控制参数大小:

  • -Xms 设置堆的最小空间;
  • -Xmx设置堆的最大空间大小;     
  • -XX:NewSize设置新生代最小空间大小。
  • -XX:MaxNewSize设置新生代最大空间大小。
  • -XX:PermSize设置永久代(方法区)最小空间大小。(JDK1.8废弃PermGen
  • -XX:MaxPermSize设置永久代(方法区)最大空间大小
  • -Xss设置每个线程的堆栈大小。
  • 老年代的空间设置:Xms(Xmx)-XX:NewSize(MaxNewSize)进行设置;

 3、GC算法 垃圾回收:                               

  3.1、对象存活条件判断:
    • 引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,释放时减1,为0时回收。但无法解决对象相互循环引用的问题;

    • 可达性分析:从GC roots开始向下搜索,搜索走过的路径称为引用链。当一个对象到GC roots没有任何引用链相连时,证明此对象是不可用的,不可达对象。                  

  3.2、GC 算法:

    • 标记-清除算法(Mark-Sweep) :第一阶段标记处所有需要回收的对象,第二阶段统一回收所有被标记的对象。
    • 复制算法(Copying)      :将可用内存按容量大小划分为相等的两块,每次只使用一块。当前块用完了,则将还活着的对象复制到另外一块上,并把当前块内存清空。
    • 标记-压缩算法                        :标记过程同Mark-Sweep算法,标记完成后,让所有存活的对象向一端移动,清理掉端边界以外的内存。
    • 分代收集(Generational Collection):将JAVA堆分为年轻代和老年代,分别采用不同的收集算法(年轻代:复制;老年代标记整理)。

  3.3、GC回收器

    垃圾回收器是对GC算法的实现:

  • Serial收集器:是最古老、最稳定、效率高的收集器。参数控制:-XX:+UseSerialGC

    • 单个线程串行GC
    • 新生代复制算法、老年代标记压缩
    • GC过程中会Stop-the-world
  • ParNew收集器:是Serial收集器的多线程版本;参数控制:-XX:+UseParNewGC  ParNew收集器 -XX:ParallelGCThreads 限制线程数量。
    • 新生代并行GC,老年代串行GC
    • 新生代复制算法、老年代标记压缩;
    • GC过程会Stop-the-world。
  • Parallel收集器:类似于ParNew,更关注系统的吞吐量(用户代码运行时间/总运行时间)
    • 新生代复制算法、老年代标记压缩;
    • 老年代串行;
  • Parallel Old收集器:参数控制: -XX:+UseParallelOldGC 使用Parallel收集器+ 老年代并行
    • 多线程+标记-整理算法
  • CMS收集器:以获取最短的回收停顿时间为目标。
    • 优点:并发收集、低停顿。
    • 缺点:大量空间碎片,并发降低吞吐量。
    • 步骤:
    1. 初始标记:仅标记GC roots直接关联到的对象,stop-the-world;
    2. 并发标记:可达性分析的过程,时间较长。
    3. 重新标记:修正阶段2因程序继续运行造成的不分对象的标记变动。stop-the-world;
    4. 并发清除:并发,时间较长。

4、GC分析 命令调优                             

  JVM调优就是根据GC的日志分析JVM的内存分配、回收的情况来调整各区域的内存比例或GC回收的策略。???

  4.1、GC日志分析

    young gc日志格式:

    full gc日志格式:

 4.2、JVM调优命令

    运用jvm自带的命令,可以方便的在生产监控和打印堆栈日志信息中帮助开发者定位问题。

    Sun的JDK监控和故障处理命令主要有以下:

    • jps:JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
    • jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
    • jmap,JVM Memory Map命令用于生成heap dump文件
    • jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
    • jstack,用于生成java虚拟机当前时刻的线程快照。
    • jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。
4.1.1:jps(JVM process status tool)显示指定系统内所有HotSpot的进程。格式: 

        jps [options] [hostid]  

        - l :输出主类全名或jar路径;
-q :只输出LVMID;
-m:输出jvm启动时传递给main()的参数;
- v:输出jvm启动时显示指定的jvm启动参数;
4.1.2:jstat(JVM statistics Monitoring)用于监视虚拟机运行时状态信息的命令,可以显示虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据;    jstat [option] LVMID [interval] [count] : 操作参数 本地虚拟机进程ID 连续输出时间间隔 连续输出的次数     jstat -gc 1262 2000 20 /*每隔2000ms输出1262的gc情况,输出20次  
-class : 显示ClassLoader的行为统计;
-compiler:输出JIT编译过的方法数量耗时;
-gc :垃圾回收堆的行为统计;
-gccapacity:输出堆的各区域的最大最小空间;
-gcutil :输出堆的各区域已用空间占总空间的百分比;
-gccause :垃圾收集统计概述;
-gcnew :统计新生代行为;
-gcnewcapacity:统计新生代的内存空间;
-gcold :统计旧生代行为;
-gcoldcapacity :统计旧生代的大小和空间;
-gcpermcapacity:永生代的行为统计;
printcompilation :hotspot编译方法统计; 4.1.3:jmap(jvm memory map)用于生成heap dump文件(进程所使用内存情况的一次快照)。        jmap [option] LVMID 4.1.4:jhat(JVM heap analysis tool)与jmap搭配,分析jmap生成的dump; 4.1.5:jstack :jstack用于生成java虚拟机当前时刻的线程快照; 4.1.6:jinfo:实时查看和调整虚拟机的运行参数。

  4.3、调优工具:

    常用调优工具分为两类,

    jdk自带监控工具:jconsolejvisualvm(jdk/bin目录下)

    第三方有:MAT(Memory Analyzer Tool)、GChisto

    • jconsole:Java Monitoring and Management Console是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控
    • jvisualvm,jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等
    • MAT,Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
    • GChisto,一款专业分析gc日志的工具

<---------------------------------------------------------------------------------------------------------->

                 

JVM的小总结(转)的更多相关文章

  1. JVM(七),JVM面试小知识

    七.JVM面试小知识 1.JVM三大性能调优参数 -Xms -Xmx -Xss 的含义 2.java内存模型中堆和栈的区别 3.不同JDK版本中的intern()方法的区别

  2. 启动 jvm 参数小总结

    1.启动某项目 nohup java -jar -Xms256m -Xmx512m -Dspring.config.location=/config/application.yml -Dfile.en ...

  3. JVM的小理解

    1.开发人员编写Java代码(.java文件),然后将之编译成字节码(.class文件),再然后字节码被装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有选择的转换成机器 ...

  4. jvm的小练习

    代码如下: public static void main(String[] args) { byte[] array= new byte[1024*1024]; array=new byte[102 ...

  5. Java 知识笔记 - 类、集合、多线程、IO、JVM(最后一次更新,2019年02月17日)

    目录 Class 内部类.静态内部类.匿名内部类.局部内部类 Collection Java Collection Set Queue Map Collections Arrays System Co ...

  6. 「每日五分钟,玩转JVM」:对象从哪来

    面向对象 众所周知,Java是一门面向对象的高级编程语言,那么现在问题来了,对象从哪来呢?有些人会说通过new关键字来创建一个对象,说的很好,本篇我们就来解密在new一个对象的过程中,JVM都给我们做 ...

  7. RocketMQ_问题_启动报错,修改堆内存大小

    1.启动broker报错 虚拟机内存小,导致虚拟机中的JVM内存小,进而在启动broker时分配JVM内存遇到问题 查询网上得知,查看/usr/local/rocketmq-all-4.3.0/dis ...

  8. LVS+keeplived+nginx+tomcat高可用、高性能jsp集群

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://kerry.blog.51cto.com/172631/557749 #!/bin ...

  9. 干货 Elasticsearch 知识点整理二

    目录 root object mate-field 元数据字段 mapping-parameters 动态mapping(dynamic mapping) 核心的数据类型 精确匹配与全文检索 精确匹配 ...

随机推荐

  1. LibOpenCM3(四) VSCode IDE 环境配置

    目录 LibOpenCM3(一) Linux下命令行开发环境配置 LibOpenCM3(二) 项目模板 Makefile分析 LibOpenCM3(三) .ld文件(连接器脚本)和startup代码说 ...

  2. MongoDB 带访问控制的副本集部署

    当你需要用到一个MongoDB 副本集集群,用于开发测试时, 可以通过下面的步骤简单完成. 版本及环境 MongoDB4.4  Centos6.5  一. 下载安装 MongoDB Server 及 ...

  3. hacker模拟环境

    https://geekprank.com/hacker/

  4. Linux下的Tmux分屏操作

    Linux中,我们使用命令行的时候,一个窗口只能使用一个命令行,若是需要使用多个输入位置进行操作,那么只能多开几个窗口,今天介绍一款软件,可以让我们在一个窗口使用多个输入行,先来效果图展示下 Linu ...

  5. [杂记]如何在LaTeX里插入高亮代码

    继上次学会在ppt里面插入带有高亮的c程序代码之后,zyy在这条不归路上越走越远-- 好的,长话短说,今天我介绍的是怎么在LaTeX中插入高亮的c程序代码.(其实不止c程序的代码,别的语言也是可以的, ...

  6. 零基础小白也能用的商业智能BI工具,自助式就是香!

    ​随着数字化时代的到来,数据已经成为企业无形的资源,企业对员工的数据分析能力也提出了新的要求.掌握一定的数据分析能力无疑会大大增加自己在职场中的竞争力,但并不是所有人都具备专业的数据分析基础,尤其是虽 ...

  7. Pandas:将DataFrame中的一列转化为List

    #假设data是一个DataFrame对象,如果要把它的第二列转换为List print(data.iloc[:,1].to_list())

  8. Qt:使用SqlQuery进行查询时size总是-1

    原因:SQL语句没有符合格式,特别是在换行写一个SQL语句时,不同行之间没有写空格

  9. PIL-ImageFont:OSError: cannot open resource

    使用PIL时,创建某个字体Font对象时出错: font=ImageFont.truetype('Arial.ttf',36) 可能原因有两个: 1.PIL无法定位到字体文件的位置. 可以通过提供绝对 ...

  10. 堪比JMeter的.Net压测工具 - Crank 入门篇

    1. 前言 Crank 是.NET 团队用来运行基准测试的基准测试基础架构,包括(但不限于)来自TechEmpower Web 框架基准测试的场景,是2021年.NET Conf 大会上介绍的一项新的 ...