栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JVM 学习笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JVM 学习笔记

*JVM 学习笔记*
    • JVM的结构体系
        • 基本组件
        • 运行流程
    • 类加载器
        • 类装载方式(两种) :
        • 类装载的执行过程
        • 四种类加载器
      • 三层的ClassLoader:
        • 我们写的Hello.java编译成的Hello.class文件,它是如何被加载到JVM中的呢?
    • 双亲委派机制
    • JVM字节码执行引擎
    • 沙箱安全机制
        • 组成沙箱的基本组件:
    • Native关键字
        • Function:
        • Example:
    • 方法区
        • Function:
        • 栈帧
      • 本地方法栈
      • Java堆
    • 栈、堆、方法区的交互
    • 堆栈的区别
    • 深拷贝和浅拷贝
    • Java会存在内存泄漏吗?
    • 引用

JVM的结构体系

基本组件

JVM包含两个子系统和两个组件: 两个子系统为Class loader(类装载)、Execution engine(执行引擎); 两个组件为Runtime data area(运行时数据区,即最大的方块)、Native Interface(本地接口)。

  • Class loader(类装载):根据给定的全限定名类名(如:java.lang.Object)来装载class文件到 - - Runtime data area中的method area。
  • Execution engine(执行引擎):执行classes中的指令。
  • Native Interface(本地接口):与native libraries交互,是其它编程语言交互的接口。
  • Runtime data area(运行时数据区域):这就是我们常说的JVM的内存。
运行流程
  • 首先通过编译器把 Java 代码转换成字节码,
  • 类加载器(ClassLoader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,
  • 因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,
  • 而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
类加载器

实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。

  • Java中的所有类,都需要由类加载器装载到JVM中才能运行。
  • 类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。
  • Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。
类装载方式(两种) :
  • 隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中
  • 显式装载, 通过class.forname()等方法,显式加载需要的类
类装载的执行过程

类装载分为以下 5 个步骤:

  • 加载:根据查找路径找到相应的 class 文件然后导入;
  • 验证:检查加载的 class 文件的正确性;
  • 准备:给类中的静态变量分配内存空间;
  • 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
  • 初始化:对静态变量和静态代码块执行初始化工作。
四种类加载器
  • 启动类加载器(Bootstrap ClassLoader): 用来加载java核心类库,无法被java程序直接引用。
  • 扩展类加载器(extensions class loader): 它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
  • 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。
三层的ClassLoader:

JVM中提供了三层的ClassLoader:

  • Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。
  • ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。
  • AppClassLoader:主要负责加载应用程序的主函数类
我们写的Hello.java编译成的Hello.class文件,它是如何被加载到JVM中的呢?
  • 在介绍双亲委派模型之前先说下类加载器。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立在 JVM 中的唯一性,每一个类加载器,都有一个独立的类名称空间。类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。
双亲委派机制

![双亲委派机制](https://img-blog.csdnimg.cn/1f629032be6c4fed8921ccc34add3210.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAZnV0dXJuX2hlcm8=,size_20,color_FFFFFF,t_70,g_se,x_16

从上图中我们就更容易理解了,当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。

JVM字节码执行引擎
  • 虚拟机核心的组件就是执行引擎,它负责执行虚拟机的字节码,一般户先进行编译成机器码后执行。

  • “虚拟机”是一个相对于“物理机”的概念,虚拟机的字节码是不能直接在物理机上运行的,需要JVM字节码执行引擎- 编译成机器码后才可在物理机上执行。

沙箱安全机制

沙箱机制就是讲Java代码限定在虚拟机JVM特定的运行范围中,并且严格限制代码对本地资源的访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。

组成沙箱的基本组件:

1.字节码校验器(bytecode verifier):确保Java类文件遵循Java语言规范。可以帮助Java程序实现内存保护 。核心类不经过字节码校验
2.类装载器:其中类装载器在3个方面对Java沙箱起作用

  • 防止恶意代码干涉善意代码(双亲委派机制)
  • 守护被信任的类库边界
  • 它将代码归入保护域,确定了代码可以进行哪些操作
Native关键字 Function:
  • 来调用底层C、C++的库
Example:
  • private native void start();
  • 进入本地方法栈
  • 调用本地方法本地接口JNI
方法区
  • 所有定义的方法的信息在此区域
  • 方法区是所有线程共享的内存区域,它用于存储已被Java虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  • 它有个别命叫Non-Heap(非堆)。当方法区无法满足内存分配需求时,抛出OutOfMemoryError异常。
静态变量、常量、类信息(构造函数,接口代码)、运行时的常量池存在方法区中,
但是实例变量存在堆内存中,和方法区无关

栈 Function:
  1. 主管程序的运行,生命周期和线程同步;
  2. Java虚拟机是线程私有的,它的生命周期和线程相同。
  3. 线程结束,栈内存释放,不存在垃圾回收问题
  4. 虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
  5. 栈:先进后出,后进先出
  6. 虚拟机栈中是有单位的,单位就是栈帧,一个方法一个栈帧。一个栈帧中他又要存储,局部变量,操作数栈,动态链接,出口等。
栈帧
  • 局部变量表:是用来存储我们临时8个基本数据类型、对象引用地址、returnAddress类型。(returnAddress中保存的是return后要执行的字节码的指令地址。)
  • 操作数栈:操作数栈就是用来操作的,例如代码中有个 i = 6*6,他在一开始的时候就会进行操作,读取我们的代码,进行计算后再放入局部变量表中去
  • 动态链接:假如我方法中,有个 service.add()方法,要链接到别的方法中去,这就是动态链接,存储链接的地方。
  • 出口:出口是什呢,出口正常的话就是return 不正常的话就是抛出异常落
本地方法栈
  • 本地方法栈很好理解,他很栈很像,只不过方法上带了 native 关键字的栈字
  • 它是虚拟机栈为虚拟机执行Java方法(也就是字节码)的服务方法
  • native关键字的方法是看不到的,必须要去oracle官网去下载才可以看的到,而且native关键字修饰的大部分源码都是C和C++的代码。
  • 本地方法栈中就是C和C++的代码
Java堆
  • Java堆(Java Heap)是java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建。
  • 此内存区域的唯一目的就是存放对象实例。
  • 在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
  • java堆是垃圾收集器管理的主要区域,因此也被成为“GC堆”。
  • 从内存回收角度来看java堆可分为:新生代和老生代。
  • 从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区。
  • 无论怎么划分,都与存放内容无关,无论哪个区域,存储的都是对象实例,进一步的划分都是为了更好的回收内存,或者更快的分配内存。
  • 根据Java虚拟机规范的规定,java堆可以处于物理上不连续的内存空间中。当前主流的虚拟机都是可扩展的(通过 -Xmx 和 -Xms 控制)。如果堆中没有内存可以完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
栈、堆、方法区的交互

  • Person:存放在元空间,也可以说方法区;
  • p:存放在Java栈的局部变量表中;
  • new Person():存放在Java堆中;
  • 栈指向堆是什么意思?就是栈中要使用成员变量怎么办,栈中不会存储成员变量,只会存储一个应用地址。
堆栈的区别


静态变量放在方法区, 静态的对象还是放在堆。

深拷贝和浅拷贝
  • 浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,
  • 深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,
  • 浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。
  • 深复制:在计算机中开辟一块新的内存地址用于存放复制的对象。
Java会存在内存泄漏吗?
  • 内存泄漏是指不再被使用的对象或者变量一直被占据在内存中。

  • 理论上来说,Java是有GC垃圾回收机制的,也就是说,不再被使用的对象,会被GC自动回收掉,自动从内存中清除。

  • 但是,即使这样,Java也还是存在着内存泄漏的情况。

  • Java导致内存泄露的原因很明确:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露。

  • 尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景。

引用

1.《深入浅出JVM》
2.《JVM》–狂神说(bilibili)
3.《通俗易懂的双亲委派机制》–IT烂笔头(CSDN)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/821036.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号