JVM王炸面试知识脑图 学弟看完进了阿里

导图地址:

https://mm.edrawsoft.cn/mobile-share/index.html?uuid=3f88d904374599-src&share_type=1

类加载器

双亲委派模型

当一个类收到类加载请求,它首先把类加载请求交给父类(如果还有父类,继续往上递交请求).如果父类无法加载该类,再交给子类加载

防止内存中出现多份同样的字节码对象 (出于安全性考虑)

保证核心.class不被篡改

如何打破双亲委派模型

1.自定义类加载器,重写loadClass方法

2.使用上下文类加载器

打破双亲委派模型的案例

Tomcat

JDBC

类加载器

应用程序类加载器 AppClassLoader

使用java语言编写
加载范围
负责加载环境变量classpath或系统属性java.class.path指定的类库
自己写的类都是由应用程序加载类加载的

扩展类加载器 ExtClassLoader

使用java语言编写
加载范围
从java.ext.dirs指定的路径下加载类库;或者从JDK安装目录的jre/lib/ext目录下加载类库
如果用户自定义的jar包放在jre/lib/ext下,也会自动由扩展类加载器加载

引导类加载器 BootStrapClassLoader

引导类加载器使用C/C++语言实现,在JVM内部
加载范围
用于加载核心库
只加载包名为java,javax,sun开头的类

JVM组成

存在线程安全问题

方法区(永久代)

方法区是所有线程共享的区域.用于存储被Java虚拟机加载的类信息,常量,静态变量,即时编译后的数据
即时编译: 为了平衡启动和执行的效率,JVM结合解释执行和编译执行的特点,进行解释执行并对热点代码进行编译优化,这样的执行过程叫即时编译
当方法区无法满足内存分配需求时,抛出OutOfMemoryError异常
1.8之后叫元空间,使用物理内存,不受jvm参数限制

堆是虚拟机管理中内存最大的一块,被所有线程共享.所有对象实例以及数组都要在堆上分配内存
堆是垃圾管理器管理的的主要区域,因此也被称为"GC堆"
GC : 这里指Java的垃圾回收机制
常量池1.8在堆中
分为新生代,老年代 内存大小比例为 1: 2

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"><html><head><meta name="qrichtext" content="1" /><style type="text/css">p, li { white-space: pre-wrap; }</style></head><body style=" font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; line-height:100%;"><span style=" font-family:'微软雅黑'; font-size:12pt;">新生代又分为Eden和Survivor (From与To,这里简称一个区)两个区。加上老年代就这三个区。数据会首先分配到Eden区当中(当然也有特殊情况,如果是大对象那么会直接放入到老年代(大对象是指需要大量连续内存空间的java对象)。当Eden没有足够空间的时候就会触发jvm发起一次Minor GC,。如果对象经过一次Minor-GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空间当中。并将其年龄设为1,对象在Survivor每熬过一次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,就会被晋升到老年代中了,当然晋升老年代的年龄是可以设置的。</span></p></body></html>

新生代
  • 一个 Eden 区
  • 两个 survival 区 ( From Survivor、To Survivor ),无论什么时候总有一块 survival 是空闲的
  • 为什么需要两个 survival 区设置两个Survivor区最大的好处就是解决了碎片化
老年代( 垃圾回收后在 survival区存活 15 (默认) 次后进入 ) , 在老年代一般使用 标记-清除算法 或者 标记整理算法
为什么要这么分代 : 可以根据各个代的特点进行对象分区存储,更便于回收,采用最适当的收集算法
新生代中,每次垃圾收集时都发现大批对象死去,只有少量对象存活,便采用了复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须采用“标记-清理”或者“标记-整理”算法。

线程私有

本地方法区

本地方法栈中就是C和C++的代码编译的方法

程序计数器

保存当前线程所正在执行的字节码指令的地址(行号)

栈是线程私有的,每个方法在执行的时候都会创建一个栈帧,方法先进后出
栈帧
  • 操作数栈操作数栈可理解为java虚拟机栈中的一个用于计算的临时数据存储区。
  • 局部变量表存放局部临时数据
  • 动态链接在程序运行期间将符号引用转为直接引用
  • 出口方法执行完毕返回的位置
JVM会为每一个线程分配一个栈帧

JVM抛出错误

OutOfMemoryError 内存溢出

出现的原因

不断创建对象,并添加到集合中,无法被回收,抛出内存溢出错误
扩展栈的时候申请不到足够的空间,抛出内存溢出错误

修改最小内存和最大内存

-Xms -Xmx

StackOverflowError 栈深度过大

递归调用方法无出口

JVM的GC垃圾回收机制

什么是GC垃圾回收机制

在系统运行过程中,会产生一些无用的对象,这些对象一直占着内存,如果不对这些对象进行清除,会导致内存资源耗尽. 所以就需要GC垃圾回收机制 回收 堆 和 方法区的内存

判断对象是否存活

引用计数法

每个对象在创建的时候,就给这个对象绑定一个计数器。每当有一个引用指向该对象时,计数器加一;每当有一个指向它的引用被删除时,计数器减一。这样,当没有引用指向该对象时,计数器为0就代表该对象死亡
引用计数法就是如果一个对象没有被任何引用指向,则可视之为垃圾。这种方法的缺点就是不能检测到环的存在
优点
引用计数算法的实现简单,判定效率也很高
缺点
不能解决循环依赖问题,所以一般不用

可达性分析法

)
优点
能解决引用计数法的循环依赖问题
缺点
和引用计数法比没有缺点

三种 GC垃圾回收机制

Minor GC

发生在新生代的垃圾回收,最频繁,速度最快
当eden区满时,触发Minor GC .当为一个对象申请内存地址时,发现eben区内存不够用就发生Minor GC

Major GC

发生在老年代的垃圾回收,通常会伴随着Minor GC ,Major GC通常伴随着 Minor GC 速度比 Minor GC慢
晋升到老年代的对象大于老年代剩余的空间内存
永久代空间不足
手动执行System.gc
MinorGC后存活的对象超过了老年代剩余空间
方法区空间不足

Full GC

清理整个堆空间,包括方法区
老年代被写满
持久代被写满
System.gc()被显示调用
会产生 STW ( stop the world ) 整个世界都停了 所以应该极力避免 Full GC

垃圾回收算法(GC回收算法)

标记-清除算法

创建对象的时候储存一个标记位,记录对象的状态(是死是活)
优点:可以解决相互循环引用问题,必要时才进行回收
缺点:标记和清除效率不高,回收时应用需要挂起,.且会造成内存碎片问题
内存碎片 : 内存中的存储空间不连续,当需要申请连续的存储空间时无法申请到

标记整理算法

创建对象的时候储存一个标记位,记录对象的状态(是死是活),但一阶段不进行对象删除,还要进行二阶段,在二阶段将存活的对象整理起来,放到另一端空间,然后再把剩下的对象全部清除
优点:解决了标记-清除算法造成的内存碎片问题
缺点:由于在二阶段移动了对象,所以需要去更新引用

复制算法

将内存平均分为两份,每次只使用其中一部分,当这部分内存满的时候,将其中存活的内存复制到另一半内存空间,然后将这部分内存清空,再去使用另一半内存空间.循环下去
复制算法与标记整理算法的区别在于 该算法不是在同一个区域复制,而是将所有存活的对象复制到另一个区域内
优点: 在存活对象不多的情况下,性能高,能解决内存碎片和java垃圾回收算法之-标记清除 中导致的引用更新问题。
缺点 : 会造成一部分的内存浪费。不过可以根据实际情况,将内存块大小比例适当调整;如果存活对象的数量比较大,复制算法的性能会变得很差

分代算法 ( JVM 使用 )

新生代对象朝生夕死,对象数量多,只要重点扫描这个区域,那么就可以大大提高垃圾收集的效率。另外老年代对象存储久,无需经常扫描老年代,避免扫描导致的开销。
新生代
在新生代,每次垃圾收集器都发现有大批对象死去,只有少量存活,采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
老年代
而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须“标记清除法或者标记整理算法进行回收

垃圾收集器

JAVA中的引用

强引用

最普遍的引用方式,当内存不足时,JVM宁愿抛出OOM异常,也不会回收强引用的对象

软引用

有用但是不是必须的对象,当JVM内存不足时会回收该对象

在Java中用java.lang.ref.SoftReference类来表示

在实际程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生

利用软引用和弱引用解决OOM问题:假如有一个应用需要读取大量的本地图片,如果每次读取图片都从硬盘读取,则会严重影响性能,但是如果全部加载到内存当中,又有可能造成内存溢出,此时使用软引用可以解决这个问题。
设计思路是:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题。

弱引用

非必须对象,进行GC的时候就会回收该对象

在java中,用java.lang.ref.WeakReference类来表示

虚引用

如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收的活动

在java中用java.lang.ref.PhantomReference类表示

Eden 区 与 survival 区 内存大小占比 默认 为 8 : 1 : 1 ,通常在新生代使用复制算法

Major GC 和 Full GC 通常是等价的

一般用于老年代回收

一般用于老年代

一般用于新生代的Eden 区和 survival 区

全部评论

相关推荐

下水道鼠鼠鼠鼠:男的能去当技师吗 好进吗
点赞 评论 收藏
分享
评论
3
8
分享
牛客网
牛客企业服务