JVM系列--基础


前言

  1. 线上full gc 频繁怎么解决?
  2. 系统隔几天就oom
  3. 说说jvm内存模型?
  4. 说说垃圾回收算法
  5. 说说类加载机制
  6. 线上故障,如何排查,分析,定位。

1. 代码是怎么跑起来的

maven打包—》class文件–》类加载器加载(双亲委派和打破双亲委派以及自定义类加载器)—》类信息加载到方法区中—》创建线程—》线程执行main方法—》加载类的相关类(用到的时候加载,验证,准备(静态),解析,初始化(赋值),使用,卸载)—》分配堆内存(对象,垃圾回收)—》分配栈内存(引用)—》方法入栈—》程序计数器记录栈帧(线程私用)–》出栈

1.类加载

2. 类加载到使用的过程

加载-》验证-》准备-》解析-》初始化-》使用-》卸载

验证阶段:校验.CLASS文件是否符合规范

准备阶段:比如给类的静态变量分配内存空间,并赋初始值

解析阶段:符合引用替换直接引用–不用深究这里

初始化阶段:执行类初始化阶段的代码了,比如静态代码块。真正赋值的是在这里,准备阶段只是给默认值。

3. 类加载器和双亲委派机制

1 java的类加载器都有哪些?

启动类加载器:加载 java安装目录下/lib/的类(bootstap classloader)

拓展类加载器:(extension classloader) 加载java安装目录下/lib/ext的类

应用程序类加载器:(application classloader) 加载 classpath 环境变量下的类

自定义类加载器: 根据自己的需求加载需要的类

2 类加载器的层级结构

在这里插入图片描述

2 什么叫双亲委派机制?为什么需要这样的机制?

先找父类加载,父类由找父类加载,一直到启动类加载器加载。然后父类如果找不到,再传递给子类加载器去加载。

①**防止加载同一个.class。**通过委托去询问上级是否已经加载过该.class,如果加载过了,则不需要重新加载。保证了数据安全。

②**保证核心.class不被篡改。**通过委托的方式,保证核心.class不被篡改,即使被篡改也不会被加载,即使被加载也不会是同一个class 对象,因为不同的加载器加载同一个.class也不是同一个Class对象。这样则保证了Class的执行安全。

沙箱安全机制 防止代码的污染

3.两个class对象是否为同一个类

在JVM中表示两个class对象是否为同一个类存在的两个必要条件

类的完整类名必须一致,包括包名
加载这个类的ClassLoader必须相同。

4 tomcat是打破双亲委派机制的,为什么?

1.保证多个实例加载不同的版本

2.相同的类库相同的版本可以共享

5.什么时候需要自定义类加载器?

(1).多个jvm进程加载不同的版本jar

(2).加密

(3).热加载以及热部署

4. jvm都有哪些内存区域?

1.方法区:(jdk1.8以后叫metaspance),存放类信息还有常量池(string 池)

2.程序计数器:执行字节码文件时(就是我们写的代码)需要内存空间记录当前字节码指令执行的位置。每一个线程一个程序计数器。

3.java虚拟机栈:每个线程内部都有存放每个方法的局部变量的内存。一个线程一个虚拟机栈。一个栈内有多个栈帧。一个方法一个栈帧。方法执行完毕,栈帧从栈里退出(出栈)。虚拟机栈的大小会直接影响可创建线程数量的大小。虚拟机栈内存越大可创建的数量越小。方法内的局部变量存放在栈内。这里如果方法内的局部变量过大,或者递归调用方法,可能回出现栈溢出。

4.堆内存:新生代(第一次new的对象)+eden(躲过yong gc 后存活的对象)+老年代的总和(大对象,或者躲过多次gc的对象)

5.本地方法栈:native方法,底层调用c++的代码。每个线程独有。

6.堆外内存:有机会了解下

5. 类加载是按需加载,可以一次性加载全部的类吗?

默认是按需加载的。手动写个类加载器可以一次性加载全部类。

6. 自定义类加载器如何实现?

自己写一个类,继承ClassLoader类,重写类加载的方法,然后在代码里面可以用自己的类加载器去针对某个路径下的类加载到内存里来


链接:https://www.zhihu.com/question/46719811/answer/1739289578

7.解决jar冲突解决方案:

https://www.zhihu.com/question/46719811?sort=created

最短路径依赖jar,来解决jar冲突问题。

这样每个中间件的classloader就可以加载各自版本的fastjson。因为一个类的全限定名以及加载该类的加载器两者共同形成了这个类在JVM中的惟一标识,这也是阿里pandora实现依赖隔离的基础。

a继承b,b继承c,但是abc的版本都不一样,这个时候,要想abc使用不同的版本,可以通过自定义类加载器来实现,打破了双亲委派机制。

通过classloader我们可以完成对变更内容的加载,然后快速的启动。

链接:https://www.zhihu.com/question/46719811/answer/1739289578

热部署本质其实与热加载并没有太大的区别,通常我们说热加载是指在开发环境中进行的classloader加载,而热部署则更多是指在线上环境使用classloader的加载机制完成业务的部署。所以这二者使用的技术并没有本质的区别。那热部署除了与热加载具有发布更快之外,还有更多的更大的优势就是具有更细的发布粒度。我们可以想像以下的一个业务场景。

img

假设某个营销投放平台涉及到4个业务方的开发,需要对会场业务进行投放。而这四个业务方的代码全部都在一个应用里面。因此某个业务方有代码变更则需要对整个应用进行发布,同时其它业务方也需要跟着回归。因此每个微小的发动,则需要走整个应用的全量发布。这种方式带来的稳定性风险估且不说,整个发布迭代的效率也可想而知了。这在整个互联网里,时间和效率就是金钱的理念下,显然是无法接受的。

那么我们完全可以通过类加载机制,将每个业务方通过一个classloader来加载。基于类的隔离机制,可以保障各个业务方的代码不会相互影响,同时也可以做到各个业务方进行独立的发布。其实在移动客户端,每个应用模块也可以基于类加载,实现插件化发布。本质上也是一个原理。
在阿里内部像阿拉丁投放平台,以及crossbow容器化平台,本质都是使用classloader的热加载技术,实现业务细粒度的开发部署以及多应用的合并部署。

2.内存结构(不是内存模型)

7. 为什么堆内的对象要区分老年代和新生代?

因为垃圾回收的算法不一样。新生代执行的是xx算法。老年代执行的xx算法。并行标记清除算法。

老年代执行的是 标记-清除算法,数量较多,存在内存碎片。内存利用率高

老年代存活对象太多,采用复制算法挪动大量对象,效率更差

新生代执行的是 复制算法,数量较少,且大部分都是短期存活的。没有内存碎片,因为清空了内存,再复制。缺点是内存利用率不高。(一个eden+2个sur)

8. 方法区是否回进行垃圾回收?

方法区和堆一样,都是线程共享的内存区域,被用于存储已被虚拟机加载的类信息(字段等)、即时编译后的代码(方法字节码)、静态变量和常量等数据。

根据Java虚拟机规范的规定,方法区无法满足内存分配需求时,也会抛出OutOfMemoryError异常,虽然规范规定虚拟机可以不实现垃圾收集,因为和堆的垃圾回收效率相比,方法区的回收效率实在太低,但是此部分内存区域也是可以被回收的。

方法区的垃圾回收主要有两种,分别是对废弃常量的回收(常量池的回收)和对无用类的回收(类的卸载)。
当一个常量对象不再任何地方被引用**的时候,则被标记为废弃常量,这个常量可以被回收。

方法区中的类需要同时满足以下三个条件才能被标记为无用的类:
1.Java堆中不存在该类的任何实例对象;

2.加载该类的类加载器已经被回收;

3.该类对应的java.lang.Class对象不在任何地方被引用,且无法在任何地方通过反射访问该类的方法。

当满足上述三个条件的类才可以被回收,但是并不是一定会被回收,需要参数进行控制,例如HotSpot虚拟机提供了-Xnoclassgc参数进行控制是否回收。

9. 老年代会执行垃圾回收吗?

大对象直接进入老年代。空间担保机制。动态年龄判断机制。

10. jvm核心参数有哪些?各表示什么意思?

Xms-堆内存大小

Xmx- 堆内存最大值

Xmn-新生代大小,扣除新生代大小就是老年代大小

xx:permSize 永久代大小 (jdk1.8以后不叫这个了,叫metaspancesize)

xx:MaxPermSize 永久代最大值(jdk1.8以后不叫这个了,叫maxmetaspancesize)

xss:每个线程的大小

11. 什么叫做软引用,弱引用?

static xxx a =new xxx(); 强引用

softreference(new xxx());软引用 --当进来垃圾回收后,内存还是不够放入新的对象,就会回收,哪怕被变量引用了。

wakreference(new xxx ());弱引用,跟没有引用是一样的。

虚引用–不管这个。很少用

12. 什么情况下JVM内存中的一个对象会被垃圾回收?

根据可达性算法来确定。

可达性算法:根据引用的持有关系,一层层往上判断,看是否有一个gcroot

局部变量可以作为gcroots

静态变量可以作为gcroots

13. 新生对象要经过多久才能进入老年代

-xx:maxtenuringthreshold 默认是15

动态年龄判断:年龄1+年龄2+…+年龄n>sur的内存大小*50%,那么这批内存年龄n以上的,都进入老年代(注意:这里是年龄n的进入老年代),其作用就是保证sur有50%的可用空间进行下次垃圾回收。

大对象直接进入老年代 -xx:pretenureThreshold 可用指定直接进入老年代

.minorgc后,存活的对象太多,无法放入sur,怎么办?—直接进入老年代。好可怕这里。

如果老年代也放不下上一步产生的对象,会怎么样?

在这里插入图片描述

老年代触发gc的两种情况:

1.minior gc 之前检查老年代空间不够用,提前触发 old gc 带着young gc

2.young gc 后,老年代内存存不下了,促发old gc 。

14. JVM中都有哪些常见的垃圾回收器,各自的特点是什么?

在这里插入图片描述

serial

​ 特性:新生代回收器

​ 优点:–windows 系统(客户端) 避免上下文切换,简单高效

​ 缺点:回收慢

​ 使用:-XX:+UseSerialGC

parnew

​ 特性:新生代回收器

​ 缺点:parnew:多线程,在单核下,不如serial ,只能以cms配合。

​ 使用:-XX:+UseParNewGC

​ 优点:多线程 缩短回收的时间

parallel scavenge

特性:新生代回收器,目标是提高吞吐量(吞吐量 = 运行用户程序的时间 / (运行用户程序的时间 + 垃圾收集的时间))。	

​ 使用:-XX:+UseParallelGC

​ 场景:大数据(少交互,多计算)

serial old:

​ 特性:老年代回收器

​ 优点:–windows 系统(客户端) 避免上下文切换,简单高效

​ 缺点:回收慢

​ 使用:-XX:+UseSerialOldGC

parallel old:

​ 特性:老年代回收器

​ 优点:–windows 系统(客户端) 避免上下文切换,简单高效

​ 缺点:回收慢

​ 使用:-XX:+UseParallelOldGC

​ 场景:大数据(少交互,多计算)

cms:CMS(Concurrent Mark Sweep)

​ 优点:垃圾回收线程几乎能做到与用户线程同时工作。

​ 缺点:

  • 吞吐量低

  • 无法处理浮动垃圾

  • 标记 - 清除算法带来的内存空间碎片问题

    使用:-XX:+UseConcMarkSweepGC

G1(新生代 + 老年代):

​ 多 CPU 和大内存的场景下有很好的性能

​ 使用复制 + 标记 - 整理算法收集新生代和老年代垃圾。

​ G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离

​ 使用:-XX:+UseG1GC

15. 如何做到只有young gc 没有 full gc

借助一些工具查看 每秒钟产生多少对象内存,多长时间触发young gc ,平均每次young gc 后存活多少内存。,sur是否放得下。

还有,避免动态年龄判断进入老年代。

16. parnew 垃圾回收器的线程是多少?cms呢?

parnew 跟系统核心数一样

cms (cpu核数+3)/4

17. CMS如何实现系统一边正常工作的同时一边进行垃圾回收的?

垃圾回收线程和工作线程尽量同时执行的模式来处理。

四个阶段:

1.初始标记–系统停止工作,速度很快,仅仅标记gc roots 直接引用的对象罢了。

2.并发标记–系统继续工作,速度很慢,很耗时,对老年代所有对象进行gc roots 追踪。

3.重新标记–系统停止工作,速度很快,重新标记1时间点后的垃圾对象和新的对象。

4.并发清理–系统继续工作,速度很慢,很耗时,

18. cms并发垃圾回收的机制问题

1.concurrent mode failure问题

​ 浮动垃圾:系统一直运行,但是有些对象虽然进入了老年代,但是已经是垃圾对象了。要等到下次垃圾回收才会执行。

​ 所以,老年代要留有一部分空间存这部分的对象,可以指定多少比例: -xx:cmsinitiatingOccupanyFaction 比例 jdk1.6默认是92%

​ 预留了8%

​ 如果系统要放入的老年代对象内存 > 老年代可用空间,–>发生concurrent mode failure(垃圾回收失败),垃圾回收器 serial old 替换

cms,强行把系统程序 stop the world。重新进行长时间的gc roots 追踪。标记出来全部垃圾对象,不允许产生新的对象。

​ 生产环境中:xx:cmsinitiatingOccupanyFaction 参数要合理优化,避免 concurrent mode failure 的问题。

2.内存碎片问题

​ 标记清理算法会产生内存碎片,容易产生太多的内存碎片导致频繁的full gc 。

​ 参数:-xx:+useCMSCompactAtFullCollection,默认打开,表示full gc 后,再次进行stop the world。进行内存碎片整理。

19. 如何减少对象进入老年代

1.动态年龄判断,基本上不用做什么操作,默认就是sur1(sur0)的50%会触发

2.大对象: -xx:pretenureThreshold =1m,可以修改

3.空间担保失败,默认开启空间担保机制。

4.大年龄对象进入老年代,默认15岁进入。

能做的:调大sur区。调大大对象参数

20.jvm垃圾回器组成:

在这里插入图片描述

21. 对象回收图示:

在这里插入图片描述

22. jvm调优的分析过程:

每秒占用多少内存,

多长时间触发一次 minor gc ?

minor gc 后存活多少对象?

surivor 放得下不?

会不会频繁的进入老年代

动态年龄判断会不会进入老年代

23. 老年代垃圾回收参数如何优化?

-xx:cmsinitiatingOccupanyFaction 比例 ,达到这个比例,会执行full gc 。 不是 old 的总内存。

发生concurrent mode failure(垃圾回收失败)会执行cms 切换到 seril old

-xx:maxtenuringthreshold 进入老年代的年龄

-xx:pretenureThreshold 大对象进入老年代

survivorRatio 动态年龄判断

handlepromotionfailure 没有打开

空间担保失败 老年代可以用内存 < 历次进入的对象

内存碎片整理:CMSFullGCsBeforeCompaction=0 ,每次都进行内存整理。

24. 4核8g的机器,能开多少个线程?

jvm进程(50+)+系统进程(50)

G1垃圾回收器

25. G1垃圾回收器的工作原理

相比parNew+cms,G1垃圾回收器的优点

parNew+cms的痛点: stop the world

老年代和新生代都是G1,

java 堆内存拆分为多个一样大小的region

region 也有老年代新生代的概念,不过是逻辑上的。

G1最大的特点是可用设定垃圾回收的预停留时间。比如:设置系统一个小时内stw的时间不超过1分钟。

是如何做到的呢?

region的回收价值:回收的大小和耗费的时间

同也给region可能某个时段是存放新生代的,可能另外一个时段存放的是老年代的。没有说老年代多大的内存,新生代多大的内存一说。

最少回收时间和最多回收对象的region进行垃圾回收。保证gc的停顿时间在可控的范围内,同时又尽可能的回收更多的对象。

26. G1的几个问题

G1是如何工作的?

对象什么时候进入新生代region?

什么时候触发新生代region 的gc?

什么时候对象进入老年代的region?

什么时候会触发老年代的region gc?

27. G1为什么回收性能比传统GC更好?

  1. 如何设定G1对应的内存大小

    有多少个region?每个region有多大?

    -xms:开始内存,-xmx:最大内存

    -xx:+useg1gc

    jvm最多有2048个region

    region的大小必须是2的倍数

    比如如果给堆的内存是4g(4096m),那么4096/2048=2m,每个region有2m大小。

    也可以通过手动指定-xx:G1HeapRegionSize来指定大小。

    刚开始新生代默认堆内存是是5%,也可以指定:xx:G1NewSizePercent设置

    新生代占比默认不超过60%,也可以指定:-xx:G1MaxNewSizePercent设置

    而且region一旦回收,新生代的region也会减少。是动态的。

  2. 新生代也有eden和surivor的概念。SuvivorRatio=8,sur区占用新生代的比例。但是 eden区和sur占用不用的region。

  3. G1的新生代的回收

    G1的新生代的垃圾回收也是一样的。区别在于,设定了指定的时间。指定时间可以通过 xx:MaxGCPauseMills来指定。

    默认是200ms。

  4. 对象什么时候进入老年代?和cms一样

  5. 大对象region

    1. 和cms有所变化:一个region的50%,就是大对象。

      ​ 大对象可以横跨多个region

    2. 大对象存放在哪里?新生代还是老年代?

      ​ 都不是,单独的存储

    3. 大对象什么时候触发垃圾回收?

      ​ young gc 或者full gc 的时候

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值