目录
1.怎么理解JVM?
Jvm 体系结构图中的每一块是干什么的? 详细展开!
首先.class文件从类加载器入口进入,
类加载器有四种:自定义类加载器,应用程序类加载器,扩展类加载器和启动类加载器。
其中使用的是双亲委派机制:当一个类的加载器接收到加载请求之后,需要去加载一个.class文件的时候,它会委托他的父类去进行加载。例如应用程序类加载器会委托扩展类加载器,扩展类加载器会委托启动类加载器进行加载,如果启动类加载不了,则会依次向下寻找,从启动类到扩展类到应用应用程序,如果都没有,则会报错。保障
好处就是可以确保JVM 的核心类 {启动类,扩展类加载器中的类不被破坏},整个过程中只会有一个字节码文件。
从执行引擎离开。负责解析命令,交给操作系统执行交给操作系统执行。本地方法接口和本地方法栈由Native修饰,调用C/C++代码,long包和Object包都是由C/C++实现的。或者线程的sleep和wait方法。
然后是运行时数据区:其中有方法区,堆,栈,程序计数器(又叫做PC寄存器)
方法区是存储类的元信息(包括Stu.class的模板)的地方
栈的生命期是跟随线程的生命期,线程结束栈内存也就释放,主要用来存储8大基本数据结构,对象的引用变量(对象的地址)和实例方法。栈的数据以栈帧存储。主要保存三类数据:本地变量,栈操作和栈帧数据。栈是先进后出。栈溢出时可能是在代码中出现了循环调用。如果没有的话可以用-Xss: 设置栈大小。
堆是在JDK7之前是新生代,养老代,和永久代。8和之后是新生代,养老代和元空间
新生代分为伊甸园区幸存者0区(From区)和幸存者1区(To区),新生区是对象的诞生、成长、消亡。当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),
MinorGC垃圾回收的过程如下:
1.eden、From 复制到 To,年龄+1 首先,当Eden区满的时候会触发第一次GC,把还活着的对象拷贝到Survivor From区,当Eden区再次触发GC的时候会扫描Eden区和From区域,对这两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到To区域(如果有对象的年龄已经达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1
2.清空 eden、Survivor From 然后,清空Eden和From中的对象
3.To和 From 互换 最后,To和From互换,原To成为下一次GC时的From区。部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活,就存入到老年代
4.大对象特殊情况 如果分配的新对象比较大Eden区放不下,但Old区可以放下时,对象会被直接分配到Old区(即没有晋升这一过程,直接到老年代了)
MinorGC的过程:复制 -> 清空 -> 互换。From区和To区总有一个是空的
经历多次GC仍然存在的对象(默认是15次)会进入老年代。若养老区也满了,那么这个时候将产生FullGC,进行养老区的内存清理。若养老区执行了Full GC之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”。如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二:
(1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。
(2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。
Full GC的速度一般会比MinorGC慢10倍以上。
FullGC 会引发 Stop The World Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互。
进入老年代有四种情况:
1.在伊甸园区存满了,进行Minor GC的时候,发现对象太大,放不进去To区,这时候会进入老年代
2.对于伊甸园区太大,或者大于伊甸园区的对象会进入老年代
3.动态对象的年龄判定,当s0中需要复制的对象中 相同年龄所有对象大小的总合 大于 s1空间的一半,那么大于等于这个年龄**的对象都会直接挪到老年代。
4.当经历过15次GC后仍然存货的对象进入老年代
永久代在JDK1.8之后就变成了元空间,永久代使用的是jvm的堆内存,但是java8以后的元空间并不在虚拟机中而是使用本机物理内存。因此,默认情况下,元空间的大小仅受本地内存限制。
2.如何解决OOM? -- 如何调优?
使用调优参数: -Xmx:最大堆内存,默认是内存的1/4 -Xms:初始堆内存,只要启动,就占用的堆大小,默认是内存的1/64 -Xmn:新生代大小 -Xss: 线程栈大小
通过参数动态指定垃圾收集器! JDK1.8: -Xmx100m -Xms10m -XX:+PrintGCDetails JDK17: -Xmx100m -Xms10m -XX:+UseG1GC -Xlog:gc* 17 的默认垃圾收集器为G1
java.lang.OutOfMemoryError: Java heap space -- 堆内存不足! 可以使用 -Xmx: -Xms: 动态调整堆内存大小! 增加物理内存! 但是,并不是越大越好!垃圾回收时间会变长,影响用户体验(STW 当GC开始时,所有的用户线程都将停止) java.lang.OutOfMemoryError: PermGen space -- 本地物理内存不足! 增加物理内存!
3.请问什么对象才可以被回收?
一共有两种方法:
第一种是引用计数法,为每个对象设置一个计数器,当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1。当计数器为0时,就可以被回收。但是在Java中不能解决循环调用的问题,所以现在只是C、Python使用,Java不使用。
第二种就是根可达分析算法:GC Root,从GC Root开始判断是否有引用链的关系,如果没有相连的话,就是证明这个是不可用的。真正标记为可回收至少要标记两次。
第一次就是不在GC Root引用链中,第二次就是看他是否重写了finalize() 方法,如果没有,则直接判定可以回收,判定这个对象可以回收。如果重写了finalize()方法,则会放在一个队列中,并由虚拟机创建一个低优先级的程序去执行他,看看是否重新启用,如果没有的话就判定可以进行回收。
在Java语言中,可以作为GC Roots的对象包括下面几种:
-
虚拟机栈(栈帧中的本地变量表)中的引用对象。 Stu stu = new Stu
-
方法区中的类静态属性引用的对象。 应用程序类加载器是静态的,也就是static修饰 的
-
方法区中的常量引用的对象。 final修饰的
-
本地方法栈中JNI(Native方法)的引用对象
两种方法: 引用计数法: 为每个对象设置一个计数器,计数器为0则表示没有引用!可以被回收! Java 不适用!Python ,C 才使用! 根可达分析算法 GC Root: 从GC Root 开始,判断当前对象是否与它存在引用链关系! 四种引用:
强引用,软引用,弱引用,虚引用
| 引用类型 | 回收时机 | 适用场景 |
|---|---|---|
| 强引用 --> Stu stu = new Stu | 永不回收,有地址(除非=null) | 日常对象引用 |
软引用 SoftReference | 内存不足时回收 | 内存敏感的高速缓存 |
弱引用 WeakReference | GC发生时立即回收 | 临时缓存,如ThreadLocal |
虚引用 PhantomReference | 无法获取对象,仅回收跟踪 | 对象回收监听机制 |
4.具体是通过什么算法进行回收的? 有哪些算法?
四种算法:复制算法,标记清除,标记整理(标记压缩),GC(分代收集算法)
复制算法:内存分两半,使用一半,存活对象复制到另一半后清空当前半区简单高效,好处:无内存碎片 缺点:浪费50%空间,对象存活率高时复制开销大
标记清除:先标记所有可回收对象,再统一清除,好处:不会浪费内存空间,缺点:会扫描两次,产生内存浪费
标记整理:标记后让所有存活对象向一端移动,然后再进行清理,好处:没有内存碎片,缺点:存货的对象需要向一端移动,浪费时间。
分代收集:分为新生代和老年代:新生代使用复制算法,老年代使用标记清除和标记整理混合使用。
四种:
1.复制算法 新生区(复合from,to; to区总为空) 优点: 简单,高效 缺点: 浪费内存,克服很多对象存活时的复制效率
2.标记清除 优点: 不会浪费内存空间 缺点: 两次扫描 内存碎片
3.标记整理 优点: 没有内存碎片 缺点: 两次扫码 存活对象向一端移动;需要时间
4.GC-分代收集算法
新生代: 复制算法!
老年代: 标记清除,标记整理混合使用!
垃圾收集器: 新生区: 复制 老年 压缩
CMS:标记清除!
5.垃圾收集器有那些?
Serial/Serial Old:单线程,新生代复制,老年代标记-压缩,会有Stop The World
ParNew:Serial的多线程版本
Parallel/Parallel old:关注吞吐量(Throughput),可自适应调节,新生代复制,老年代标记-压缩
CMS:专注最短回收停顿时间,基于“标记-清除”算法实现
-
初始标记:标记GC Roots直接关联对象(STW,极快)
-
并发标记:GC Roots Tracing(与用户线程并发,最长)
-
重新标记:修正并发标记期间变动的对象(STW,比初始标记稍长)
-
并发清除:清理垃圾(与用户线程并发)
优点:并发收集,低停顿,缺点: 产生内存碎片,吞吐量降低
G1:
-
利用多核cpu的优势使原本需要停顿的Java 线程变为可执行的!
-
G1能够独立管理新生代、老年代;而CMS 只能管理老年代!需要与ParNew配合使用!
-
G1使用域的概念来管理整个堆内存,不会产生内存碎片
-
能够有自己的可预测停顿时间模型,在一定的时间片段内,控制垃圾回收的时间
1338

被折叠的 条评论
为什么被折叠?



