十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
本篇内容介绍了“JVM三大组成部分是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
专注于为中小企业提供成都网站建设、成都网站制作服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业泰山免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了超过千家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
JVM三大组成部分:类加载子系统、运行时数据区、执行引擎
1.程序计数器(线程私有)
当前线程所执行的字节码的行号指示器。
2.虚拟机栈(线程私有)
每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态
链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
3.本地方法栈(线程私有)
为虚拟机使用到的Native 方法(底层C编写的方法)服务。
4.堆(线程共享)
此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。是垃圾收集器管理的主要区域,也被称作“GC堆”。
堆大小 = 新生代 + 老年代。其中,堆的大小可以通过参数 –Xms、-Xmx 、 -Xmn 来指定。
默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。
其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。
默认的,Edem : from : to = 8 :1 : 1 ( 可以通过参数–XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。
5.方法区--JDK1.8改为元空间---非堆内存(线程共享)
存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值。
1.JVM如何确定垃圾?GC ROOT(可达性分析)
虚拟机栈(栈帧中的局部变量表)中引用的对象。
木地方法栈中JNI(Native方法)引用的对象。
方法区中的类静态属性引用的对象。
方法区中常量引用的对象。
2.JVM调优步骤
2.1 打印GC日志
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps,Tomcat则直接加在JAVA_OPTS变量里
-XX:+HeapDumpOnOutOfMemoryError让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分析
2.2 分析日志得到关键性指标
2.3 分析GC原因,调优JVM参数
3.JVM查看系统默认值
jps:查看进程号,jps -l
jinfo:查看默认值,jinfo -flag 配置项 进程号/jinfo -flags 进程号
java -XX:+PrintFlagsFinal version
-XX:+PrintFlagsInitial 查看初始值
-XX:+PrintFlagsFinal 查看修改更新
-XX:+PrintCommandLineFlags 默认的垃圾回收器
4.JVM常用基本参数配置
-Xms:初始大小内存,默认为物理内存1/64 等价于-XX:InitialHeapSize
-Xmx:最大分配内存,默认为物理内存的1/4 等价于-XX:MaxHeapSize
-Xss:设置单个线程栈的大小,一般默认为512k~1024k 等价于-XX:ThreadStackSize
-Xmn:设置年轻代大小
-XX:MetaspaceSize: 元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。
-XX:PrintGCDetails:打印GC情况 (查看:名称 GC前内存占用->GC后内存占用 该区内存总大小)
-XX:SurvivorRatio:设置新生代中eden/ S0/S1空间的比例;默认-XX:SurvivorRatio=8,即Eden:S0:S1 = 8:1:1;SurvivorRatio的值就是设置eden区的比例占比,S0/S1相同。
-XX:NewRatio:配置年轻代与老年代在堆中的占比,默认-XX:NewRatio=2,新生代占1,老年代占2,年轻代占整个堆的1/3;NewRatio值就是设置老年代的占比,新生代为1。
-XX:MaxTenuringThreshold:设置垃圾最大年龄,默认为15,且最大不能超过15。
5.垃圾回收算法和垃圾收集器(GC算法是方法论,垃圾收集器就是算法的落地实现)
四种垃圾回收算法
1.引用计数:引用时+1,反之亦然,为0时,表示可回收(循环引用有问题)
2.复制(Copy):实际可使用的内存空间缩小为原来的一半(浪费空间、耗时)
将内存按容量划分为两块,每次只使用其中一块。当这一块内存用完了,就将存活的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。这样使得每次都是对半个内存区回收,也不用考虑内存碎片问题,简单高效。缺点需要两倍的内存空间。
3.标记清除(Mark Sweep):产生内存碎片
GC分为两个阶段,标记和清除。首先标记所有可回收的对象,在标记完成后统一回收所有被标记的对象。同时会产生不连续的内存碎片。碎片过多会导致以后程序运行时需要分配较大对象时,无法找到足够的连续内存,而不得已再次触发GC。
4.标记整理(Mark Compact):压缩整理效率低(耗时)
也分为两个阶段,首先标记可回收的对象,再将存活的对象都向一端移动,然后清理掉边界以外的内存。此方法避免标记-清除算法的碎片问题,同时也避免了复制算法的空间问题。
扩:一般年轻代中执行GC后,会有少量的对象存活,就会选用复制算法,只要付出少量的存活对象复制成本就可以完成收集。而老年代中因为对象存活率高,没有额外过多内存空间分配,就需要使用标记-清除或者标记-整理算法来进行回收。
四种垃圾回收器
Serial串行:它为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有线程。所以不适合生产环境
Parallel并行:多个垃圾收集线程并行工作,此时用户线程是暂停的,适用于科学计算/大数据处理首台处理等弱交互场景
CMS并发:用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程(停顿短,可忽略),适用于对响应时间有要求的场景
* 初始标记(Initial Mark)
* 并发标记(Concurrent Mark)
* 重新标记(Remark)
* 并发清除(Concurrent Sweep)
其中,初始标记、重新标记这两个步骤仍然需要暂停应用线程。初始标记只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段是标记可回收对象,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作导致标记产生变动的那一部分对象的标记记录,这个阶段暂停时间比初始标记阶段稍长一点,但远比并发标记时间短。
由于整个过程中消耗最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,CMS收集器内存回收与用户一起并发执行的,大大减少了暂停时间。
G1:将堆内存划分多个大小相等的独立区域(Region),并且能预测暂停时间,能预测原因它能避免对整个堆进行全区收集。G1跟踪各个Region里的垃圾堆积价值大小(所获得空间大小以及回收所需时间),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region,从而保证了在有限时间内获得更高的收集效率。
G1收集器工作工程分为4个步骤,包括:
* 初始标记(Initial Mark)
* 并发标记(Concurrent Mark)
* 最终标记(Final Mark)
* 筛选回收(Live Data Counting and Evacuation)
初始标记与CMS一样,标记一下GC Roots能直接关联到的对象。并发标记从GC Root开始标记存活对象,这个阶段耗时比较长,但也可以与应用线程并发执行。而最终标记也是为了修正在并发标记期间因用户程序继续运作而导致标记产生变化的那一部分标记记录。最后在筛选回收阶段对各个Region回收价值和成本进行排序,根据用户所期望的GC暂停时间来执行回收。
扩:
1.G1比较CMS的优势:
1)G1不会产生内存碎片
2)可以精确控制停顿。该收集器是把整个堆(新生代、老生代)划分成多个固定大小的区域,每次根据允许停顿的时间去收集圾最多的区域
2.垃圾收集器组合:默认的垃圾收集器-Parallel Scavenge
6.为什么分代?
将对象根据存活概率进行分类,对存活时间长的对象,放到固定区,从而减少扫描垃圾时间及Full GC频率。
针对分类进行不同的垃圾回收算法,对算法扬长避短。注:Full GC会引发STW(stop the world)
7.为什么survivor分为两块相等大小的幸存空间?
主要为了解决碎片化。如果内存碎片化严重,也就是两个对象占用不连续的内存,已有的连续内存不够新对象存放,就会触发GC。
8.为什么会堆内存溢出?
在年轻代中经过GC后还存活的对象会被复制到老年代中。当老年代空间不足时,JVM会对老年代进行完全的垃圾回收(Full GC)。
如果GC后,还是无法存放从Survivor区复制过来的对象,就会出现OOM(Out of Memory)。
OOM(Out of Memory)异常常见有以下几个原因:
1)老年代内存不足:java.lang.OutOfMemoryError:Javaheapspace
2)永久代内存不足:java.lang.OutOfMemoryError:PermGenspace
3)内存泄漏:代码bug,占用内存无法及时回收。
OOM在这几个内存区都有可能出现,实际遇到OOM时,能根据异常信息定位到哪个区的内存溢出。
可以通过添加个参数-XX:+HeapDumpOnOutMemoryError,让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后分析。
9.cpu过高问题排查
故障现象:用户反馈网页卡顿严重,登录2分钟...
9.1.先用top命令查看资源占用情况,找出cpu占比最高的
top
9.2.ps -ef或者jps进一步定位进程
ps -ef | grep java
9.3.定位到具体线程或者代码
top -Hp pid
ps -mp pid -o THREAD,tid,time
-m 显示所有线程
-p 显示在pid列表中的进程
-o 该参数后是用户自定义格式
9.4.将需要的线程ID转换为16进制
printf "%x\n" tid
9.5.jstack 打印线程堆栈信息
jstack 进程ID | grep 线程id(16进制线程ID小写英文) -A60(表示查找到所在行的后60行)
扩展
参数类型:1.标配,例:java -version ,java -help 2.-X参数(了解),例:-Xlint解释执行,-Xcomp第一次使用就编译成本地代码(先编译) 3.-XX
finalize: 是一个 java.lang.Object 里的方法 ,当一个对象被gc时,调用,可覆盖该方法,重新给对象赋值,即不会被回收。当垃圾收集确定没有对对象的更多引用时,由对象上的垃圾收集器调用。一个子类覆盖finalize方法来处理系统资源或执行其他清理。
“JVM三大组成部分是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注创新互联网站,小编将为大家输出更多高质量的实用文章!