• 初探JVM


    五道常问面试题

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

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

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

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

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

    1.JVM的位置

    2.JVM的体系结构

    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)和其他自定义的类加载器。这些类加载器可以根据需要进行扩展和定制,以满足特定的加载需求。

     

    4.双亲委派机制

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

    Class not found~

    百 度   双亲委派机制

    native关键字:

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

    5.沙箱安全机制

    6.Native

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

    1. /**
    2. * native:凡是带了native 关键字的,说明java的作用范围达不到了,回去调用底层c语言的库!
    3. * 会进入本地方法栈
    4. * 调用本地方法本地接口 俗称 JNI java native interface
    5. * JNI作用:扩展Java的使用,融合不同的编程语言为Java所用! 最初:C,C++!
    6. * Java诞生的时候 C ,C++横行,想要立足,必须要有调用C,C++的程序~
    7. * 所以它在内存区域中专门开辟了一块标记区域:Native Method Stack。用于登记 native 方法
    8. * 在最终执行的时候,调用本地方法接口去找到本地方法库中的方法去加载
    9. *
    10. * Java程序驱动打印机,管理系统,掌握即可,在企业级应用中较为少见
    11. * Robot()这个类里就有大部分native方法,因为它是操作操作系统的。
    12. */
    13. 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中的存储位置取决于其类型。对于基本类型的常量(如整数、浮点数等),它们的值直接存储在方法区的常量池中。而对于引用类型的常量(如字符串、数组等),常量池中存储的是对象的引用,而对象本身则存储在堆内存中。

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

    static.final.class.常量池 

    9.栈

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

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

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

    10.三种JVM

    • Sun公司 就是我们现在用的

    11.堆

    12.新生区.老年区

    13.永久区

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

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

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

    方法区是一个特殊的堆

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

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

    14.堆内存调优

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

    1. package com.qf.jvmStudy.nativeDemo;
    2. public class Demo03 {
    3. public static void main(String[] args) {
    4. //返回虚拟机试图使用的最大内存
    5. long max = Runtime.getRuntime().maxMemory();//字节*1024*1024=MB
    6. //返回JVM的总内存
    7. long totalMemory = Runtime.getRuntime().totalMemory();
    8. //默认情况下:分配的最大内存 是电脑内存的1/4 ,而初始化的内存:1/64
    9. System.out.println("max=+"+max+"字节\t"+(max/(double)1024/1024)+"MB");
    10. System.out.println("totalMemory=+"+totalMemory+"字节\t"+(totalMemory/(double)1024/1024)+"MB");
    11. //虚拟机试图使用的最大内存 JVM实际用的总内存 打印GC细节信息
    12. //-Xmx1024m -Xms1000m -XX:+PrintGCDetails
    13. //OOM: 堆内存满了:我们先尝试把堆内存扩大,如果扩大同样的代码还报同样的错,分析内存,看一下哪个地方出现了问题(专业工具)
    14. // 305664K+699392K= 1,005,056k=981.5MB totalMemory=+1029177344字节 981.5MB
    15. }
    16. }

     

    1. package com.qf.jvmStudy.nativeDemo;
    2. import java.util.ArrayList;
    3. //-Xmx1m -Xms1m -XX:+HeapDumpOnOutOfMemoryError
    4. public class Demo04 {
    5. public static void main(String[] args) {
    6. ArrayList demo04s = new ArrayList<>();
    7. try {
    8. while (true){
    9. demo04s.add(new Demo04());
    10. }
    11. } catch (Exception e) {
    12. e.printStackTrace();
    13. }
    14. }
    15. }

     

     

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

     

      //-Xmx1024m -Xms1000m -XX:+PrintGCDetails  //打印GC回收信息

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

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

     

    15.GC :垃圾回收

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

    • 新生代(伊甸园)

    • 幸存区(from区 to区)

    • 老年区         

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

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

    GC题目:

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

    引用计数法

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

     1.常用算法

    复制算法

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

     minor GC不会OOM吧,只有Full GC也没法清理出足够内存响应申请才会OOM

       标记清除算法

    碎片就是内存一块一块的 

    标记压缩

         再优化:

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

    16.JMM

    Java Memory Model(Java内存模型)

     

    17.总结

    1.百度

    2.思维导图

     

     

     难点与易混淆点

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

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

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

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

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

    单点登录~ 架构师

  • 相关阅读:
    Insight h2database 执行计划评估以及 Selectivity
    rabbitMQ:绑定Exchange发送和接收消息(topic)
    C++&QT day4
    PyTorch中torch.gather()函数
    PHP 数组在底层的实现原理
    <list>——《C++初阶》
    [代码学习]einsum详解
    【送书活动】网络安全(黑客)自学
    java开发中 防止重复提交的几种方案
    CPU密集型和IO密集型任务的权衡:如何找到最佳平衡点
  • 原文地址:https://blog.csdn.net/qq_53374893/article/details/132699115