jvm的相关理解

说一下类加载的执行过程?

类加载分为以下 5 个步骤:

加载:根据查找路径找到相应的 class 文件然后导入;

检查:检查加载的 class 文件的正确性;

准备:给类中的静态变量分配内存空间;

解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;

初始化:对静态变量和静态代码块执行初始化工作。

怎么判断对象是否可以被回收?

一般有两种方法来判断:

引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;

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

说一下 jvm 有哪些垃圾回收算法?

标记-清除算法

标记-整理算法

复制算法

分代算法

GC垃圾回收过程

jdk8中将整个Java堆分为
新生代和老年代。其中新生区存放新生的对象或者年龄不大的对象,老年代则存放老年对象。

新生代分为Eden区(新生区)、Survivor0区(幸存区)、Survivor1区,Survivor0和Survivor1也被称为from和to区域,他们是两块大小相等并且可以互相角色的空间。

绝大多数情况下,对象首先分配在eden区,在新生代回收后,如果对象还存活,则进入to区,from区中对象如果存活也进入to区,达到一定年龄后则进去老年代,之后每经过一次新生代回收,如果对象存活则它的年龄就加1,对象达到一定的年龄后,则进入老年代

常用jvm调优参数

默认垃圾收集器 UseParallelGC

-Xms2g:初始化推大小为 2g;默认情况初始化的内存为电脑的1/64

-Xmx2g:堆最大内存为 2g;默认情况初始化内存为电脑的1/4

-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;

-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;

–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;

-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;

-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;

-XX:CMSFullGCsBeforeCompaction,设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的

-XX:+PrintGC:开启打印 gc 信息;

-XX:+PrintGCDetails:打印 gc 详细信息

-XX:+HeapDumpOnOutOfMemoryError抛出OOM异常,去分析异常文件

-XX:MaxTenuringThreshold=15设置进入老年代年龄阈值

java堆理解

堆大小最大值和最小值设置一样的好处

一般情况下,在生产环境中,初始最小堆大小-Xms与最大堆大小-Xmx被设置为相等。假设在生产环境中,初始堆大小-Xms与最大堆大小-Xmx是不等的,那么JVM就会根据堆内存的使用情况,动态的向操作系统申请内存,扩大或者是缩小,以-Xmx和-Xms的值为上下界,这里的每一次调整都会产生一定的系统开销,虽然做到了动态申请堆大小的能力,不过生产环境中,很少说一台机器跑好多个JAVA程序,一般情况下都是一对一,那么动态申请调整堆大小就没有意义了,因为不管内存申请的多还是少,都只是这个JAVA程序在用,不需要给其他的程序腾出空间,相反的,如果把初始堆大小-Xms与最大堆大小-Xmx设置成不相等,那么反而画蛇添足,因为如果初始堆大小-Xms与最大堆大小-Xmx不相等,那么就会需要申请空间时,而每次申请空间,就会产生相应的系统开销,同时如果一开始堆大小是-Xms,会增加程序运行时进行垃圾回收的次数,降低程序的性能。

空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC后调整堆的大小。

空间理解

新生区,老年区,元空间(逻辑上存在,物理上不存在)1.8后元空间存储于本地内存,不属于jvm

  • 类:诞生,成长甚至死亡的地方。(活的足够长的类,可能会进入老年区,但是大部分在新生区就玩完了。)

  • 伊甸园区,所有的对象一开始都是在伊甸园区new出来的。

  • 幸存者区(0,1)

  • 新生区的对象满了,就放入老年区。

  • 统计上:绝大部分的对象都是临时对象,很少有对象能活到老年区。

总内存空间=新生区+老年区

元空间:用来存放JDK自身携带的Class对象,Interface原数据。简单理解为java运行时的环境或类信息。这个区域不存在垃圾回收!关闭JVM虚拟机就会释放这个区域的内存。

栈堆理解

堆内存用于存放由new创建的对象或数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名,或者代号

如何设置年轻代和老年代的比例?

  • 更大的年轻代必然导致更小的年老代,大的年轻代会增加每次GC的时间,但会延长普通GC的周期;小的老年代会导致更频繁的Full GC
  • 更小的年轻代必然导致更大老年代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的老年代会减少Full GC的频率
  • 如何选择应该依赖应用程序对象生命周期的分布情况:如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,老年代应该适当增大。

但很多应用都没有这样明显的特性,在抉择时应该根据以下两点:(A)本着Full GC尽量少的原则,让老年代尽量缓存常用对象,JVM的默认比例1:2也是这个道理 (B)通过观察应用一段时间,看其他在峰值时老年代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给年老代至少预留1/3的增长空间

为什么新生代都是复制算法

因为新生代对象生存时间比较短,80%都是要回收的对象,采用标记-清除算法则内存空间碎片化严重,采用复制算法可以灵活高效,且便与整理空间。

老年代都是标记整理算法

标记整理算法可以解决标记-清除算法的内存碎片化的问题,又解决了复制算法的两个Survivor区的问题,因为老年代的空间比较大,不可能采用复制算法,特别占用内存空间

为什么要设置两个Survivor区

首先看下复制算法:Survivor区,一块叫From,一块叫To,对象存在Eden和From块。当进行GC时,Eden存活的对象全移到To块,而From中,存活的对象按年龄值确定去向,当达到一定值(年龄阈值,通过-XX:MaxTenuringThreshold可设置)的对象会移到老年代中,没有达到值的复制到To区,经过GC后,Eden和From被清空。之后,From和To交换角色,新的From即为原来的To块,新的To块即为原来的From块,且新的To块中对象年龄加1。永远有一个survivor space是空的,另一个非空的survivor space无碎片。

为什么要这么做?

  • 没有Survivor区,eden区对象直接去老年代,造成老年代频繁Full GC。
  • 一个Survivor容易使得Survivor的空间碎片化,因为eden区有存活对象,Survivor区也有存活对象,辅助的时候就会存在碎片化空间。
  • 即若只分一块Survivor,在清除Survivor区已死亡的对象时,因为此刻的Survivor区还有存活的对象,清除要比分两块Survivor麻烦
  • 两块的情况,回收时只需将存活的对象移走,剩下的对象直接清理即可。
  • 另外,分成两块Survivor,From和To分工明确,逻辑理解和技术实现较简单。

遇到OOM如何处理

1.尝试扩大堆内存

2.如果扩大后堆内存还是溢出,则代码有问题,分析代码

3.java运行时设置**-XX:+HeapDumpOnOutOfMemoryError** 参数抛出OOM异常,也可抛出其他类型异常,然后通过jprofiler软件分析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值