JVM入门(上)

面试常见

  1. 请你谈谈你对JVM的理解?
  2. java8虚拟机和之前的变化更新?
  3. 什么是OOM,什么是栈溢出StackOverFlowError? 怎么分析?
  4. JVM的常用调优参数有哪些?
  5. 内存快照如何抓取?怎么分析Dump文件?
  6. 谈谈JVM中,类加载器你的认识?

1、JVM的位置

在这里插入图片描述

三种JVM:

  • Sun公司:HotSpot 用的最多
  • BEA:JRockit
  • IBM:J9VM

2、JVM的体系结构

在这里插入图片描述

  • JVM调优:99%都是在方法区和堆,大部分是在调堆。 JNI(java native interface)本地方法接口。

JVM架构图

在这里插入图片描述

3、类加载器

作用:加载Class文件——例如:new Student();(具体实例在堆里,引用变量名在栈里,栈里存地址指向堆)

类加载到JVM的基本结构图

在这里插入图片描述

  • 类是模板,对象是具体的,通过new来实例化对象。car1,car2,car3,名字在栈里面,真正的实例,具体的数据在堆里面,栈只是引用地址。
  • 类是模板,只有一份,实例可以有多个
  1. 虚拟机自带的加载器
  2. 启动类(根)加载器
  3. 扩展类加载器
  4. 应用程序加载器

示例

/**
 * @author cVzhanshi
 * @create 2021-10-20 14:59
 */
public class Car {
    
    


    public static void main(String[] args) {
    
    
        Car car1 = new Car();
        Car car2 = new Car();
        Car car3 = new Car();

        System.out.println("==================实例====================");
        System.out.println(car1.hashCode());
        System.out.println(car2.hashCode());
        System.out.println(car3.hashCode());

        System.out.println("==================Class====================");
        System.out.println(car1.getClass().hashCode());
        System.out.println(car2.getClass().hashCode());
        System.out.println(car3.getClass().hashCode());
    }
}

在这里插入图片描述

查看类加载器

/**
 * @author cVzhanshi
 * @create 2021-10-20 14:59
 */
public class Car {
    
    


    public static void main(String[] args) {
    
    
        Car car1 = new Car();
        Class<? extends Car> aClass = car1.getClass();
        ClassLoader classLoader = aClass.getClassLoader();
        System.out.println(classLoader);   // AppClassLoader
        System.out.println(classLoader.getParent()); // ExtClassLoader    ---->  /jre/lib/ext
        System.out.println(classLoader.getParent().getParent()); // null 1、不存在  2、Java程序获取不到 ----> rt.jar
    }
}

在这里插入图片描述

类加载器的分类:(由上至下)

  • Bootstrap ClassLoader 启动类加载器(根加载器)
    • 主要负责加载Java核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等
  • Extention ClassLoader 标准扩展类加载器
    • 主要负责加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件
  • Application ClassLoader 应用类加载器
    • 主要负责加载当前应用的classpath下的所有类
  • User ClassLoader 用户自定义类加载器
    • 用户自定义的类加载器,可加载指定路径的class文件

4、双亲委派机制

案例引入

创建package => java.lang

创建类String

package java.lang;

/**
 * @author cVzhanshi
 * @create 2021-10-20 15:40
 */
public class String {
    
    
    public String toString() {
    
    
        return "Hello";
    }
    public static void main(String[] args) {
    
    
        String s = new String();
//        System.out.println(s.getClass());
        s.toString();
    }
}

在这里插入图片描述

可是我们明明创建了java.lang.String,且里面有Main方法

导致原因:双亲委派机制

案例解释:在运行一个类之前,首先会在应用程序加载器(APP)中找,如果APP中有这个类,继续向上在扩展类加载器EXC中找,然后再向上,在启动类( 根 )加载器BOOT中找。如果在BOOT中有这个类的话,最终执行的就是根加载器中的。如果BOOT中没有的话,就会倒着找往回找。运行案例时,需要加载java.lang.String.Class,它会把这个任务委托给他的上级类加载器,结果在根加载器有这个Class,所以就加载了根加载器的String.class,而这个String的并不是我们自己写的String,所以里面没有Main方法

案例过程总结

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

双亲委派机制概述

  • 当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类

加载流程

在这里插入图片描述

双亲委派机制作用

  • 防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
  • 保证核心.class不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。

例如:如果有人想替换系统级别的类:String.java。篡改它的实现,在这种机制下这些系统的类已经被Bootstrap classLoader加载过了(为什么?因为当一个类需要加载的时候,最先去尝试加载的就是BootstrapClassLoader),所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。

双亲委派机制详情(面试问题)

面试官:java双亲委派机制及作用

5、沙箱安全机制

Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将Java代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。

所有的Java程序运行都可以指定沙箱,可以定制安全策略。

组成沙箱的基本组件:

  • 字节码校验器(bytecode verifier)︰确保Java类文件遵循lava语言规范。这样可以帮助lava程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。
  • 类装载器(class loader) :其中类装载器在3个方面对Java沙箱起作用:
    • 它防止恶意代码去干涉善意的代码;
    • 它守护了被信任的类库边界;
    • 它将代码归入保护域,确定了代码可以进行哪些操作。

虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由Java虚拟机为每一个类装载器维护的,它们互相之间甚至不可见。

类装载器采用的机制是双亲委派模式。

1.从最内层VM自带类加载器开始加载,外层恶意同名类得不到加载从而无法使用;

2.由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内层类,破坏代码就自然无法生效。

  • 存取控制器(access controller)︰存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。
  • 安全管理器(security manager)︰是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。
  • 安全软件包(security package) : java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:
    • 安全提供者
    • 消息摘要
    • 数字签名
    • 加密
    • 鉴别

6、Native

  • 凡是带了native关键字的,说明 java的作用范围达不到,去调用底层C/C++语言的库!
  • JNI:Java Native Interface(Java本地方法接口)
  • 凡是带了native关键字的方法就会进入本地方法栈;
    • Native Method Stack 本地方法栈
  • 本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java在诞生的时候是C/C++横行的时候,想要立足,必须有调用C、C++的程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是 在 Native Method Stack 中登记native方法,在 ( ExecutionEngine ) 执行引擎执行的时候加载Native Libraies。
  • 目前该方法使用的越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机或者Java系统管理生产设备,在企业级应用中已经比较少见。因为现在的异构领域间通信很发达,比如可以使用Socket通信,也可以使用Web Service等等,不多做介绍!

举例:Thread类中的start() 方法中调用一个start0()的native方法。

7、PC寄存器

程序计数器:Program Counter Register

  • 每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向像一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。如果正在执行的是Native方法,这个计数器值则为空(Unfined)
  • 此内存区域是唯一一个在Java虚拟机规范中没有规定仍和OOM情况的区域

8、方法区

Method Area 方法区

  • 方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间;
  • 静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关
  • static ,final ,Class ,常量池~

案例图解

/**
 * @author cVzhanshi
 * @create 2021-10-21 11:23
 */
public class TestMethod {
    
    
    private String name = "cvzhanshi";
    private int age;

    public static void main(String[] args) {
    
    
        
    }
}

在这里插入图片描述

9、栈

  • 栈:后进先出 / 先进后出
  • 队列:先进先出(FIFO : First Input First Output)

喝多了吐就是栈,吃多了拉就是队列

图解,为什么main先执行,最后结束~

在这里插入图片描述

栈概述

  • 栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放

  • 对于栈来说不存在垃圾回收问题,只要线程一旦结束,该栈就Over,生命周期和线程一致,是线程私有的

  • 方法自己调自己就会导致栈溢出(递归死循环测试)。

    /**
     * @author cVzhanshi
     * @create 2021-10-21 11:23
     */
    public class TestMethod {
          
          
        private String name = "cvzhanshi";
        private int age;
    
        public static void main(String[] args) {
          
          
            new TestMethod().a();
        }
    
        public void a(){
          
          
            test();
        }
        public void test(){
          
          
            a();
        }
    }
    

    在这里插入图片描述

    图解:

    在这里插入图片描述


栈管理程序运行

  • 存储一些基本类型的值、对象的引用、方法等。
  • 栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。

栈里面会放什么东西呢?

  • 8大基本类型 + 对象的引用 + 实例的方法

栈运行原理

  • Java栈的组成元素——栈帧。
  • 栈帧是一种用于帮助虚拟机执行方法调用与方法执行的数据结构。他是独立于线程的,一个线程有自己的一个栈帧。封装了方法的局部变量表、动态链接信息、方法的返回地址以及操作数栈等信息。
  • 第一个方法从调用开始到执行完成,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程

当一个方法A被调用时就产生了一个栈帧F1,并被压入到栈中,A方法又调用了B方法,于是产生了栈帧F2也被压入栈中,B方法又调用了C方法,于是产生栈帧F3也被压入栈中 执行完毕后,先弹出F3, 然后弹出F2,在弹出F1………

  • 遵循 “先进后出” / “后进先出” 的原则。
  • 栈满了,抛出异常:stackOverflowError
  • 程序正在执行的方法,一定在栈的顶部

在这里插入图片描述


在这里插入图片描述

对象实例化的过程(在内存中)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_45408390/article/details/122293755