jvm面经

你知道的垃圾回收算法

java虚拟机运行的流程

堆区如何分类

Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年
代。

垃圾回收器

JDK8默认的回收器是: Parallel GC

Minor GC: 回收新生代
Major GC:回收老年代
Full GC: 全部回收,会Stop the world很耗时,线上环境要尽量减少Full GC

JVM中具体的垃圾回收实现有7种:

新生代: Serial、ParNew、Parallel Scavenege
老年代: Serial Old 、 Parallel Old 、CMS
堆回收: G1

垃圾回收有哪些算法

* 	引用计数
* 	复制算法
* 	标记-清除
* 	标记-整理

1.Mark-Sweep(标记-清除)算法
       这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。具体过程如下图所示:
[image:7EFCE7FC-BDDA-48C0-AA73-231269BCD656-27259-0000113F7C95CA3E/181024382398115.jpg]
从图中可以很容易看出标记-清除算法实现起来比较容易,但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。
2.Copying(复制)算法
      为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。具体过程如下图所示:
[image:6CBDB759-DA30-4F59-857B-8A45E615F043-27259-0000113F7C916653/181041528488728.jpg]
这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。
很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。
3.Mark-Compact(标记-整理)算法
        为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。具体过程如下图所示:
[image:13258D69-D941-4856-916F-B01DE55BCC3F-27259-0000113F7C8C7F67/181100129575916.jpg]
4.Generational Collection(分代收集)算法
      分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。
      目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。
       而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。
注意,在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。

java虚拟机的核心设计想???

java运行时数据区

jvm的五块

程序计数器的作用

程序计数器的作用是记录执行的字节码的位置,说白了就是代码执行到哪里了。
有什么用呢?这就得说代码在执行时,处理器一次在某一个时刻只能执行一个线程中的一行代码,所以其他线程代码的执行就会暂时停止,等到处理器可以执行其他线程的代码时就会用到程序计数器记录的位置信息,让代码继续执行下去。
所以,每个线程都私有一个程序计数器,各个线程的程序计数器相互独立互不影响。
如果线程正在执行的是一个Java方法,那么程序计数器记录的是字节码的地址,如果执行的是Native方法,那么程序计数器的值为空(Undefined)。
此区域是唯一一个不会有OutOfMemoryError的区域

虚拟机栈里放了什么

是描述 java 方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame) 用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成 的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接 (Dynamic Linking)、 方法返回值和异常分派( Dispatch Exception)。栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异 常)都算作方法结束。

[image:AC4E8022-8CF8-4C7F-809D-25F556F275FB-27259-0000114184E9613F/IMG_1876.PNG]
[image:9E66D530-093D-4A78-8C68-BFAF95A4C5FA-27259-00001141948D1A61/IMG_1877.PNG]
[image:EA20F4B6-CC32-48CA-B4BB-C41A99DE7A8B-27259-00001141BBF8EAD0/IMG_1875.PNG]

本地方法栈和虚拟机栈的区别

本地方法栈与虚拟机栈发挥的作用是非常相似的,他们之间的区别不过是虚拟机栈位虚拟机执行java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。在虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它,。甚至有的虚拟机直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutofMemoryError异常。

类加载器的双亲委派

当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父
类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,
只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的
Class),子类加载器才会尝试自己去加载。

为什么需要双亲委派

采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载
器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载
器最终得到的都是同样一个 Object 对象。

双亲委派的好处: 1)保护了核心代码不背篡改(因为所有的类最后都会到父顶级父加载器那去加载一遍,如果加载过了就不会加载); 2)保证了所有的类只会被加载一次,避免重复加载

CMS收集器和G1收集器的区别

区别一: 使用范围不一样
    CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用
    G1收集器收集范围是老年代和新生代。不需要结合其他收集器使用
区别二: STW的时间
CMS收集器以最小的停顿时间为目标的收集器。
G1收集器可预测垃圾回收的停顿时间(建立可预测的停顿时间模型)
 
区别三: 垃圾碎片
CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
G1收集器使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。
 
区别四: 垃圾回收的过程不一样
CMS收集器*                   *G1收集器

  1. 初始标记                   1.初始标记
  2. 并发标记                   2. 并发标记
  3. 重新标记                   3. 最终标记
  4. 并发清楚                   4. 筛选回收

怎么查看java虚拟机内存占用?

  • RunTime.getRunTime().maxMemory();查看JVM虚拟机可以向当前所在宿主的操作系统"可以"申请到的最大内存;如果程序运行时设置了-Xmx参数那么这个值等于用户设置值,如果没有设置默认为64m;
  • RunTime.getRunTime().totalMemory();查看JVM虚拟机当前"已经"申请到的内存大小;如果程序运行时设置了-Xms参数那么这个值等于用户设置值,如果没有设置那么程序会根据当前系统运行的情况自动申请,但这个值必定小于等于RunTime.getTime().maxMemory;
  • RunTime.getRunTime().freeMemory();查看JVM中目前已经申请的到的内存中未使用的内存部分;这个值根据当前JVM申请到的内存使用的越来越多的话,那么这个值就会越小;

12、JAVA内存如何分配?

Java 内存空间分为五个区域:
* 栈(存储局部变量)
局部变量:在方法定义中或者方法声明上的变量。
栈内存中的变量在用完(脱离作用域)后立即消失。
* 堆(存储 new 出来的东西)
堆内存的特点:
1、每一个 new 出来的东西都有地址值;
2、每个变量都有默认值
        byte、short、int、long 默认值为0
        float、double 默认值为 0.0
        char 默认值为 ‘\u0000’
        boolean 默认值为 false
        引用类型默认值为 null
 3、变量使用完毕后就变成垃圾,不会立即被回收,会在垃圾回收器空闲的时候被会回收。栈内存中的变量在用完(脱离作用域)后立即消失。
* 方法区
* 本地方法区(系统相关)
* 寄存器(CPU 使用)

13、堆区内存如何分配?

Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年
代。

方法区是否需要gc

也会gc,

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

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

方法区的垃圾回收主要有两种,分别是对废弃常量的回收(常量池的回收)和对无用类的回收(类的卸载)。

当一个常量对象不再任何地方被引用的时候,则被标记为废弃常量,这个常量可以被回收。

类加载的流程。

JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化,

知道哪些类加载器。

  1. 启动类加载器(Bootstrap ClassLoader)
    负责加载 JAVA_HOME\lib 目录中的,或通Xbootclasspath 参数指定路径中的,且被虚拟机认可(按文件名识别,如 rt.jar)的类
  2. 扩展类加载器(Extension ClassLoader)
    负责加载 JAVA_HOME\lib\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类 库。
  3. 应用程序类加载器(Application ClassLoader):
    负责加载用户路径(classpath)上的类库。
    JVM 通过双亲委派模型进行类的加载,当然我们也可以通过继承 java.lang.ClassLoader
    实现自定义的类加载器。

20、类加载器之间的关系?

21.一个对象从进入堆区到死亡的全流程

gc的过程

[image:A83C036C-906C-4B08-985E-BE30DEBC9E19-27259-0000113E49208721/IMG_1879.PNG]

system.gc()是否可以立即触发gc

调用System.gc时,系统建议执行Full GC,但是不必然执行

3 如何立刻触发gc()

Minor GC ,Full GC 触发条件
Minor GC触发条件:当Eden区满时,触发Minor GC。
Full GC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法去空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

什么条件下回触发MinorGC 和Full GC?
答: 当创建对象的时候Eden的空间不足则触发MinorGC,MinorGC非常频繁,一般回收速度也比较快,Survivor满不会引发MinorGC
当老年代空间不足的时候会触发Full GC,Full GC 会同时将老年代和新生代的垃圾进行回收。Full GC又叫做Major GC

Java GC机制?GC Roots有哪些?

* 	栈
* 	本地方法栈(Native方法)
* 	方法区:常量
* 	方法区:静态变量

JVM内存模型?每个区是做什么的?垃圾回收机制?

如何确保新生代对象被老年代引用的时候不被gc?(查询老年代对象来确认对新生代对象的引用避免误回收)

机制:当老年代存活对象多时,每次minor gc查询老年代所有对象影响gc效率(因为gc stop-the-world),所以在老年代有一个write barrier(写屏障)来管理的card table(卡表),card table存放了所有老年代对象对新生代对象的引用。
所以每次minor gc通过查询card table来避免查询整个老年代,以此来提高gc性能。

如何减少gc的停顿时间

  • 减少堆的大小
  • 加gc现成
  • 使用g1调整参数。XX:MaxGCPauseMillis(最大停顿时间)

如何减少gc的次数

  • 加大堆内存
  • 调大年轻代的阈值,老年代调了没用。
    [image:51CDD707-4C40-4744-AE58-3FDA2B7607BB-27259-0000114145DCD622/IMG_1878.PNG]

如何选择应该依赖应用程序对象生命周期的分布情况:

如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,年老代应该适当增大。但很多应用都没有这样明显的特性,在抉择时应该根据以下两点:

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

如何减少full gc的次数?

子夫进程间参数传递

我的复习笔记-JVM & 垃圾回收 & 类加载机制

https://juejin.im/post/5e383da56fb9a07ccb7e8392

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值