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一般发生在老年代

相关推荐
程序猿20239 小时前
MAT(memory analyzer tool)主要功能
jvm
期待のcode12 小时前
Java虚拟机的非堆内存
java·开发语言·jvm
jmxwzy16 小时前
JVM(java虚拟机)
jvm
Maỿbe16 小时前
JVM中的类加载&&Minor GC与Full GC
jvm
人道领域17 小时前
【零基础学java】(等待唤醒机制,线程池补充)
java·开发语言·jvm
小突突突17 小时前
浅谈JVM
jvm
饺子大魔王的男人19 小时前
远程调试总碰壁?局域网成 “绊脚石”?Remote JVM Debug与cpolar的合作让效率飙升
网络·jvm
天“码”行空1 天前
java面向对象的三大特性之一多态
java·开发语言·jvm
独自破碎E1 天前
JVM的内存区域是怎么划分的?
jvm
期待のcode1 天前
认识Java虚拟机
java·开发语言·jvm