初探JVM

五道常问面试题

1.请你谈谈你对JVM的理解?java8虚拟机和之前的变化更新?

2.什么是OOM,什么是栈溢出StackOverFlowError?怎么分析?

3.JVM的常用调优参数有哪些?

4.内存快照如何抓取,怎么分析Dump文件?知道吗?

5.谈谈JVM中,类加载器你的认识?rt-jar ext application

1.JVM的位置

初探JVM_第1张图片

2.JVM的体系结构

初探JVM_第2张图片

3.类加载器

Java有三种主要的类加载器:

1. 启动类加载器(Bootstrap Class Loader):它是Java虚拟机的一部分,负责加载Java的核心类库,如java.lang包中的类。它是用本地代码实现的,不继承自java.lang.ClassLoader。

2. 扩展类加载器(Extension Class Loader):它是由sun.misc.Launcher$ExtClassLoader实现的,负责加载Java的扩展类库,位于JRE的lib/ext目录下。

3. 应用程序类加载器(Application Class Loader):它是由sun.misc.Launcher$AppClassLoader实现的,也被称为系统类加载器。它负责加载应用程序类路径(classpath)下的类,包括开发者自定义的类。

除了这三种主要的类加载器,还有其他一些特殊的类加载器,如安全类加载器(Security Class Loader)和其他自定义的类加载器。这些类加载器可以根据需要进行扩展和定制,以满足特定的加载需求。

初探JVM_第3张图片

初探JVM_第4张图片

初探JVM_第5张图片

初探JVM_第6张图片初探JVM_第7张图片 

初探JVM_第8张图片

4.双亲委派机制

初探JVM_第9张图片

  1. 类加载器收到类加载的请求!
  2. 将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器
  3. 启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则,抛出异常,通知了加载器进行加载
  4. 重复步骤

Class not found~

百 度   双亲委派机制

native关键字:

比如线程:start()方法里有个start0()它是native修饰的,它是java实现不了,需要调用本地方法,JVM在操作系统之上,用接口调用本地方法,想要调用底层需要用native关键字

5.沙箱安全机制

初探JVM_第10张图片

初探JVM_第11张图片

初探JVM_第12张图片

初探JVM_第13张图片

初探JVM_第14张图片

初探JVM_第15张图片

6.Native

JNI:java native interface (java本地接口)

初探JVM_第16张图片

   /**
     *  native:凡是带了native 关键字的,说明java的作用范围达不到了,回去调用底层c语言的库!
     * 会进入本地方法栈
     * 调用本地方法本地接口 俗称 JNI java native interface
     * JNI作用:扩展Java的使用,融合不同的编程语言为Java所用! 最初:C,C++!
     * Java诞生的时候 C ,C++横行,想要立足,必须要有调用C,C++的程序~
     * 所以它在内存区域中专门开辟了一块标记区域:Native Method Stack。用于登记 native 方法
     * 在最终执行的时候,调用本地方法接口去找到本地方法库中的方法去加载
     * 
     * Java程序驱动打印机,管理系统,掌握即可,在企业级应用中较为少见
     * Robot()这个类里就有大部分native方法,因为它是操作操作系统的。
     */
    private native void start0();

其实调用其他语言,有很多种方法,别学死了

调用其他接口:

  1. restful风格
  2.  webSocket
  3. WebService
  4. http~

举个例子

球球爱心网:-->输入(PHP) --> nodeJS-->Socket-->C++-->游戏送爱心

7.PC寄存器

程序计数器:Program Counter Register  

 每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也是即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。

这就是为什么线程有1,2,3号线程这一说法

8.方法区

Method Area 方法区

    方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间

    静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关

Java字节码对象存储在内存中的方法区(Method Area)中。方法区是Java虚拟机(JVM)的一部分,用于存储类的结构信息,包括类的字节码、常量池、字段和方法信息等。在JVM启动时,方法区就会被创建,并且在整个程序运行期间都存在。

常量在Java中的存储位置取决于其类型。对于基本类型的常量(如整数、浮点数等),它们的值直接存储在方法区的常量池中。而对于引用类型的常量(如字符串、数组等),常量池中存储的是对象的引用,而对象本身则存储在堆内存中。

方法区的常量池是一块特殊的内存区域,用于存储类的常量、静态变量、字符串字面量等。常量池在编译阶段就被确定,并且在类加载时被加载到方法区中。由于常量的值在编译期间就确定了,而且在运行时不可修改,所以将常量存储在方法区是合理的选择。这样可以节省堆内存的空间,并且提高常量的访问效率。

初探JVM_第17张图片

static.final.class.常量池 

9.栈

调用静态方法时会将一个帧(frame)压入栈中。当调用静态方法时,会创建一个新的栈帧来存储局部变量、方法参数和其他执行该方法所需的数据。这个栈帧被推入栈中,一旦方法执行完成,该帧就会从栈中弹出。

初探JVM_第18张图片

初探JVM_第19张图片

初探JVM_第20张图片

初探JVM_第21张图片

画出一个对象实例化的过程在内存中:百度、看视频~

初探JVM_第22张图片

需要去查:如何栈里东西去存?细节? 

10.三种JVM

  • Sun公司 就是我们现在用的初探JVM_第23张图片

11.堆

初探JVM_第24张图片

初探JVM_第25张图片

初探JVM_第26张图片

初探JVM_第27张图片

12.新生区.老年区

初探JVM_第28张图片

13.永久区

这个区域是常驻内存的区域。用来存放JDK自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境或类信息~,这个区域不存在垃圾回收!关闭JVM虚拟机就会释放这个区域的内存~

一个启动类,加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的反射类,不断的被加载,直到内存满,就会出现OOM  Out of Memory;

  • JDK1.6之前:永久代,常量池是在方法区中;
  • JDK 1.7: 永久代,但是慢慢退化了,去永久代, 常量池在堆中
  • JDK 1.8 之后: 无永久代,常量池在元空间

方法区是一个特殊的堆

元空间和永久代是方法区具体的落地实现

所以常量池 静态变量 Class在元空间里面

14.堆内存调优

初探JVM_第29张图片

初探JVM_第30张图片

元空间:在逻辑上存在,在物理上不存在,因为它本身就是堆空间的一部分

package com.qf.jvmStudy.nativeDemo;

public class Demo03 {
    public static void main(String[] args) {
        //返回虚拟机试图使用的最大内存
        long max = Runtime.getRuntime().maxMemory();//字节*1024*1024=MB
        //返回JVM的总内存
        long totalMemory = Runtime.getRuntime().totalMemory();

        //默认情况下:分配的最大内存 是电脑内存的1/4 ,而初始化的内存:1/64
        System.out.println("max=+"+max+"字节\t"+(max/(double)1024/1024)+"MB");
        System.out.println("totalMemory=+"+totalMemory+"字节\t"+(totalMemory/(double)1024/1024)+"MB");
    //虚拟机试图使用的最大内存   JVM实际用的总内存   打印GC细节信息
    //-Xmx1024m -Xms1000m -XX:+PrintGCDetails

        //OOM: 堆内存满了:我们先尝试把堆内存扩大,如果扩大同样的代码还报同样的错,分析内存,看一下哪个地方出现了问题(专业工具)
     // 305664K+699392K=  1,005,056k=981.5MB     totalMemory=+1029177344字节	981.5MB
    }

}

初探JVM_第31张图片 

package com.qf.jvmStudy.nativeDemo;

import java.util.ArrayList;
//-Xmx1m -Xms1m -XX:+HeapDumpOnOutOfMemoryError
public class Demo04 {
    public static void main(String[] args) {
        ArrayList demo04s = new ArrayList<>();

        try {
            while (true){
                demo04s.add(new Demo04());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

初探JVM_第32张图片 

项目上线看不到具体报错行,只能dump后进行分析

初探JVM_第33张图片

 

初探JVM_第34张图片  //-Xmx1024m -Xms1000m -XX:+PrintGCDetails  //打印GC回收信息

//-Xmx1m -Xms1m -XX:+HeapDumpOnOutOfMemoryError  //OOM :Dump

Runtime类,就是JVM用来调优的类,打印运行时的一些环境的一些东西

初探JVM_第35张图片 

15.GC :垃圾回收

初探JVM_第36张图片

JVM在进行GC时,并不是对这三个区域统一回收。大部分的时候,回收都是新生代~

  • 新生代(伊甸园)

  • 幸存区(from区 to区)

  • 老年区         

GC两种类:轻GC(普通的GC) 重GC(全局的GC)

轻GC偶尔在幸存区清理,基本上在伊甸园

GC题目:

  • JVM的内存模型和分区~详细到每个区放什么?
  • 堆里面的分区有哪些?Eden,from ,to ,老年区,说说它们的特点!
  • GC的算法有哪些? 标记清除法,标记压缩,复制算法,引用计数法,怎么用的?
  • 轻GC和重GC分别在什么时候发生?

引用计数法

没讲全,引用计数法是对象被引用了计数器+1,引用结束-1,计数器为0就会被回收

初探JVM_第37张图片

 1.常用算法

复制算法

初探JVM_第38张图片

新生代划分为3块区域Eden、From Survior、ToSurvior,内存比例为8:1:1。

初探JVM_第39张图片

初探JVM_第40张图片 minor GC不会OOM吧,只有Full GC也没法清理出足够内存响应申请才会OOM

   标记清除算法

初探JVM_第41张图片

碎片就是内存一块一块的 

初探JVM_第42张图片

标记压缩

     再优化:

初探JVM_第43张图片

 再优化:每清除标记五次,标记压缩一次。

16.JMM

Java Memory Model(Java内存模型)

初探JVM_第44张图片

初探JVM_第45张图片 

17.总结

初探JVM_第46张图片

1.百度

2.思维导图

初探JVM_第47张图片

初探JVM_第48张图片 

 

 难点与易混淆点

元空间不在JVM虚拟机内存里。他用的是本地内存。

JVM为什么用元空间替换了永久代?

第一、1.7版本永久代内存是有上限的,虽然我们可以通过参数去设定,但是JVM加载的Class总数大小是很难去确定的,所以很容易出现OOM的一个问题,但是元空间是存储在本地内存里面,内存上限是比较大的,可以很好的去避免这个问题。

第二、 永久代的对象 是通过FullGC进行垃圾回收的,也就是和老年代同时实现垃圾回收,替换成元空间简化了FullGC的这样一个过程,可以在不进行暂停的情况下,去并发的释放类的数据,同时也提升了GC的一个性能

第三、Oracle要合并Hotspot和JRockit的一个代码,而JRockit里没有永久代,更好的实现合并

单点登录~ 架构师

你可能感兴趣的:(JVM,jvm)