JVM的小总结(转)
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.2:类的初始化时机:
- new创建类的实例时;
- 访问某个类或接口的静态变量,或者对该静态变量赋值;
- 调用类的静态方法;
- 反射(Class.forName("com.wht.Test"));
- 初始化某个类的子类时,父类也会进行初始化;
- 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空间
- 老年代
- 年轻代(8:1:1)
- 方法区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 :来自博客园,解释的很好,简单明了;
说明:
- 对象优先分配在Eden区,Eden区是连续的内存空间,如果Eden去没有足够的空间,JVM进行一次minor GC或Young GC(即从年轻代回收内存),将剩余的对象复制到一个Survivor区;
- 大对象直接进入老年代(需要大量连续内存空间的对象),以避免在Eden和两个survivor区间的大量内存拷贝。(新生代采用复制算法收集内存)
- 长期存活的对象进入老年代。JVM为每一个对象定义了一个年龄计数器,如果经过1次minor GC后,则进入survivor区,之后每一次Minor GC,该对象的年龄加1,直到达到阈值进老年区。
- 动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,那年龄大于或等于该年龄的对象进入老年区。
- 空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区剩余值的大小,则进行一次Full GC;如果小于检查HandlePromotionFailure设置,如果出现true则只进行Monitor GC,如果false,则进行Full GC。
注:另外网上还有另一种回答:
- Survivor切换:每次的Minor GC都会将剩余对象添加到该Survivor区,当该Survivor满了以后,将其中活着的复制进另一个Survivor区,以后的Minor GC将或者的对象添加到切换后的Survivor区,两个Survivor总有一个是空的。
- 进入老年代:当在两个Survivor间切换了几次(HotSpot默认15次),将其中仍然存活的对象,复制到老年代。
- 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收集器:以获取最短的回收停顿时间为目标。
- 优点:并发收集、低停顿。
- 缺点:大量空间碎片,并发降低吞吐量。
- 步骤:
- 初始标记:仅标记GC roots直接关联到的对象,stop-the-world;
- 并发标记:可达性分析的过程,时间较长。
- 重新标记:修正阶段2因程序继续运行造成的不分对象的标记变动。stop-the-world;
- 并发清除:并发,时间较长。
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自带监控工具:jconsole和jvisualvm(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的小总结(转)的更多相关文章
- JVM(七),JVM面试小知识
七.JVM面试小知识 1.JVM三大性能调优参数 -Xms -Xmx -Xss 的含义 2.java内存模型中堆和栈的区别 3.不同JDK版本中的intern()方法的区别
- 启动 jvm 参数小总结
1.启动某项目 nohup java -jar -Xms256m -Xmx512m -Dspring.config.location=/config/application.yml -Dfile.en ...
- JVM的小理解
1.开发人员编写Java代码(.java文件),然后将之编译成字节码(.class文件),再然后字节码被装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有选择的转换成机器 ...
- jvm的小练习
代码如下: public static void main(String[] args) { byte[] array= new byte[1024*1024]; array=new byte[102 ...
- Java 知识笔记 - 类、集合、多线程、IO、JVM(最后一次更新,2019年02月17日)
目录 Class 内部类.静态内部类.匿名内部类.局部内部类 Collection Java Collection Set Queue Map Collections Arrays System Co ...
- 「每日五分钟,玩转JVM」:对象从哪来
面向对象 众所周知,Java是一门面向对象的高级编程语言,那么现在问题来了,对象从哪来呢?有些人会说通过new关键字来创建一个对象,说的很好,本篇我们就来解密在new一个对象的过程中,JVM都给我们做 ...
- RocketMQ_问题_启动报错,修改堆内存大小
1.启动broker报错 虚拟机内存小,导致虚拟机中的JVM内存小,进而在启动broker时分配JVM内存遇到问题 查询网上得知,查看/usr/local/rocketmq-all-4.3.0/dis ...
- LVS+keeplived+nginx+tomcat高可用、高性能jsp集群
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://kerry.blog.51cto.com/172631/557749 #!/bin ...
- 干货 Elasticsearch 知识点整理二
目录 root object mate-field 元数据字段 mapping-parameters 动态mapping(dynamic mapping) 核心的数据类型 精确匹配与全文检索 精确匹配 ...
随机推荐
- 【C# .Net GC】手动监视和控制对象的生命周期(GCHandle)
这个话题还未做详细研究,暂时用不到,只是粗略看了一下. 使用System.Runtime.InteropServices.GCHandle类来手动控制对象的生命周期 (个人感觉这里可能有一些问题... ...
- 面向对象的基本特征:封装(接口 、struct、枚举、委托、匿名函数) 继承,多态。
如何理解面向对象的基本特征:封装 我们通过接口 .struct.枚举.委托.泛型.匿名函数的去理解封装 接口 .struct.枚举.委托.泛型.匿名函数有什么区别?我们通过这些IL探究真相,案例如下: ...
- 【C#基础概念】常量
常量的定义 常量是在编译时设置其值并且永远不能更改其值的字段. 使用常量可以为特殊值提供有意义的名称,而不是数字文本 常量是不可变的值,在编译时是已知的,在程序的生命周期内不会改变. 常量使用 con ...
- python 中的迭代器和生成器简单介绍
可迭代对象和迭代器 迭代(iterate)意味着重复,就像 for 循环迭代序列和字典那样,但实际上也可使用 for 循环迭代其他对象:实现了方法 __iter__ 的对象(迭代器协议的基础). __ ...
- 记一次加密的ts视频下载
想要下载一个视频,但是网站上不能直接下载.试过IDM,以及Streaming Video Recorder 都不行.因为视频被加密了. ts 是分片的视频文件,m3u8 是播放索引列表(还可配置其他参 ...
- Qt:输出为CSV文件时汉字乱码
参考 (18条消息) QT5写csv文件,文件打开后中文显示乱码的问题解决_yanzi150207348的博客-CSDN博客 解决方法 1.在文件开头写一段: #if _MSC_VER >= 1 ...
- Qt:QDir
0.说明 QDir提供了访问目录及目录下内容的类. QDir既可以用于访问文件系统,也可以用于访问Qt 资源系统(Qt's resource system.). Qt用 "/" 作 ...
- python实用脚本-通过jenkins界面化导出数据
1.jenkins 配置 2.jenkins 脚本 ansible-playbook /opt/test.yaml --extra-vars "loanno=${loanno}" ...
- 我完成了10000小时开发3D引擎
为什么要开始10000小时? 我以前看过一本叫<异类>的书,书的大概意思是:只要学习10000小时,任何人都可以成为一个领域的大师.这里的"学习"是指完全专注地精进学习 ...
- Drools 规则引擎应用
规则引擎-drools 1 .场景 1.1需求 商城系统消费赠送积分 100元以下, 不加分 100元-500元 加100分 500元-1000元 加500分 1000元 以上 加1000分 .... ...