JVM入门篇(面试前速补)

近期看看JVM,看了狂神说入门教学,总结下给大家。

文章目录

1、JVM的位置

JVM处于操作系统之上,为Java程序在不同的系统平台上的运行提供便利,与硬件没有直接的交互。

2、JVM的结构体系

引用地址存在栈内,实际指向的内容存在堆中。

3、类加载器及双亲委派机制

3.1、类加载器作用

3.2、类加载器类型

  • 虚拟机自带的加载器

  • 启动器(根)加载器

  • 扩展类加载器

  • 应用程序加载器

3.3、双亲委派机制 *

类加载器负责加载类的字节码并创建对应的Class对象。双亲委派机制是指当一个类加载器收到类加载请求时,它会先将该请求委派给它的父类加载器去尝试加载。只有当父类加载器无法加载该类时,子类加载器才会尝试加载。

优点:

避免重复加载:通过委派给父类加载器,可以避免同一个类被多次加载,提高了加载效率。

安全性:通过双亲委派机制,核心类库由根加载器加载,可以确保核心类库的安全性,防止恶意代码替换核心类。

扩展性:开发人员可以自定义类加载器,实现特定的加载策略,从而扩展Java的类加载机制。

缺点:

灵活性受限:双亲委派机制对于某些特殊的类加载需求可能过于严格,限制了加载器的灵活性。

破坏隔离性:如果自定义类加载器不遵循双亲委派机制,可能会破坏类加载的隔离性,导致类冲突或安全性问题。

不适合动态更新:由于类加载器在加载类时会先检查父加载器是否已加载,因此在动态更新类时可能会出现问题,需要额外
的处理。

4、沙箱安全机制


5、Native、方法区

5.1、Native(本地方法栈引用) *

5.2、PC寄存器

5.3、方法区 *

static、final、Class模板、常量池

从Java 8开始,常量池从方法区转移到Java虚拟机堆内存之中

6、栈

栈:先进后出

队列:先进先出(FIFO:First Input First Output)

堆、栈、方法区:(从Java 8开始,常量池从方法区转移到Java虚拟机内存之中)

7、走近HotSpot和堆

Heap,一个JVM只有一个堆内存,堆内存的大小是可以调整的。

类加载器读取了类文件后,一般会把什么东西放到堆中呢?

类、方法、常量、变量~,保存我们所有引用类型的真实对象(实例对象)

堆内存中细分为三个区域:

  • 新生区(伊甸园区) Young/New

  • 养老区 old

  • 永久区 Perm

JDK8以后,永久存储区改名为元空间逻辑上存在(构思存在),物理上不存在

GC垃圾回收,主要是在伊甸园区和养老区~

假设内存满了,OOM,堆内存不够!

设置VM参数

public static void main(String[] args) {
    // 获取虚拟机试图使用最大内存
    long max = Runtime.getRuntime().maxMemory();
    // 获取Jvm内存
    long total = Runtime.getRuntime().totalMemory();

    System.out.println("max memory :" + max+" 字节,"+max/(double)1024/1024+"MB");
    System.out.println("total memory :" + total+" 字节,"+total/(double)1024/1024+"MB");
}

修改前:
max memory :3309305856 字节,3156.0MB
total memory :223346688 字节,213.0MB

修改后:(添加VM配置 -Xms1024m -Xmx1024m -XX:+PrintGCDetails)
max memory :1029177344 字节,981.5MB
total memory :1029177344 字节,981.5MB
Heap
 PSYoungGen      total 305664K, used 15729K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)
  eden space 262144K, 6% used [0x00000000eab00000,0x00000000eba5c420,0x00000000fab00000)
  from space 43520K, 0% used [0x00000000fd580000,0x00000000fd580000,0x0000000100000000)
  to   space 43520K, 0% used [0x00000000fab00000,0x00000000fab00000,0x00000000fd580000)
 ParOldGen       total 699392K, used 0K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)
  object space 699392K, 0% used [0x00000000c0000000,0x00000000c0000000,0x00000000eab00000)
 Metaspace       used 3432K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 370K, capacity 388K, committed 512K, reserved 1048576K

8、使用JPofiler工具分析OOM原因

下载JPofiler工具,安装idea的JPofiler插件

//-Xms 设置初始化内存分配大小
//-Xmx 设置最大分配内存
//-XX:+PrintGCDetails  打印GC垃圾回收信息
//-XX:+HeapDumpOnOutOfMemoryError  OOM时拿到内存快照文件
public class OOMTest {

    public static void main(String[] args) {

        ArrayList<OOMTest> list = new ArrayList<>();
        int count = 0;

        // 创建数组,一直扩大
        while (true) {
            list.add(new OOMTest());
            count ++;
        }
    }
}

VM参数加入 -Xms1m -Xmx2m -XX:+HeapDumpOnOutOfMemoryError (OOM时拿到内存快照文件)

运行后在项目根目录能拿到内存快照文件 ,打开后可以看线程转储这部分分析

由此可得出具体报错位置

9、GC算法 *

9.1、引用计数法

9.2、复制算法

  • 好处:没有内存碎片
  • 坏处:浪费空间,多了一半内存空间永远是空to
  • 使用场景:对象存活率较低的情况下(新生区)

9.3、标记压缩清除法

标记清除法

优点:不需要额外的空间

缺点:两次扫描,严重浪费时间,会产生内存碎片

优化:增加压缩(标记压缩算法)

总结

内存效率:复制算法>标记清除>标记压缩(时间复杂度)

内存整齐度:复制算法=标记压缩算法>标记清除算法

内存利用率:标记压缩算法=标记清除算法>复制算法

没有最优算法。只有最合适的算法。

所以GC的好处:分代收集算法

年轻代:存活率低,用复制算法

老年代:存活率高,区域大,用标记清除+标记压缩混合实现(标记清除n次,进行标记压缩)

面试点

JVM的内存模型和分区~详细到每个区放什么?

JVM 分为堆区和栈区,还有方法区

堆区 存对象实例、数组、常量池(从Java 8开始,常量池从方法区转移到Java虚拟机堆内存之中)等

栈区 存引用地址,八大基础数据类型(局部变量),实例方法

方法区 存 static、final、Class模板
堆里面的分区有哪些?Eden,form,to,老年区,说说他们的特点?

堆分为年轻代(伊甸园区,幸存区0和1)和老年代两个分区(元空间是逻辑上存在)

Eden区(伊甸园区)是年轻代中最大的一个区域,用于存放新创建的对象。当一个对象被创建时,首先会被分配到Eden区。当满员触发GC存活对象会放置幸存区;

Survivor区是年轻代中的两个区域之一,一般称为From区和To区。伊甸园区GC存活的的会转至幸存区的From区,下次GC时若存活则复制至To区(谁空或谁少为to区)

老年区是年轻代中对象经过多次轻GC仍然存活的对象所存放的区域,老年区的对象生命周期较长,因此老年区的垃圾回收频率较低,当需要GC是重GC(Full
GC),会对整个堆内存进行回收。

GC的算法有哪些?标记清除法,标记压缩,复制算法,引用计数法,怎么用?

GC(分代收集法)的算法(GC的作用域是堆和方法区):标记清除法、标记压缩法、复制算法、引用计数法

引用计数法:各个对象每用一次,该对象计数器就+1(计数器本身也要有消耗)。当计数器为0时,说明该对象没有,就立刻进行垃圾回收。

复制算法:(年轻代主要用到复制算法:Eden区和幸存者区,要知道幸存区分为from和to两个区域:哪一个区域是空哪一个区域就是to区。)

标记清除:先扫描对象,对活着的对象进行标记。然后开始清除,对没有标记的对象进行清除

标记压缩:在标记清除的基础上再次扫描,向一端移动所有存活的对象(减少了内存碎片)
轻GC和重GC分别在什么时候发生?

法、引用计数法**

引用计数法:各个对象每用一次,该对象计数器就+1(计数器本身也要有消耗)。当计数器为0时,说明该对象没有,就立刻进行垃圾回收。

复制算法:(年轻代主要用到复制算法:Eden区和幸存者区,要知道幸存区分为from和to两个区域:哪一个区域是空哪一个区域就是to区。)

标记清除:先扫描对象,对活着的对象进行标记。然后开始清除,对没有标记的对象进行清除

标记压缩:在标记清除的基础上再次扫描,向一端移动所有存活的对象(减少了内存碎片)
轻GC和重GC分别在什么时候发生?

轻GC一般发生在 新生代和幸存区,重GC一般发生在老年代

相关推荐
阿伟*rui4 小时前
jvm入门
jvm
学点东西吧.7 小时前
JVM(五、垃圾回收器)
jvm
请你打开电视看看10 小时前
Jvm知识点
jvm
程序猿进阶10 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
阿龟在奔跑1 天前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
王佑辉1 天前
【jvm】方法区常用参数有哪些
jvm
王佑辉1 天前
【jvm】HotSpot中方法区的演进
jvm
Domain-zhuo1 天前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式
Theodore_10222 天前
7 设计模式原则之合成复用原则
java·开发语言·jvm·设计模式·java-ee·合成复用原则
我是苏苏2 天前
Web开发:ORM框架之使用Freesql的DbFrist封装常见功能
java·前端·jvm