JVM知识点(一)

这篇具有很好参考价值的文章主要介绍了JVM知识点(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、JVM基础概念

(1)JVM、JRE、JDK
  • JRE:JVM+基本类库组成的运行环境就是JRE。JVM自己是无法完成一次编译,处处运行的,需要有一个基本类库告诉JVM如何操作运行,如如何操作文件,连接网络等,JVM运行时,会一次性加载基本类库;
  • JDK:JDK中除了包含JRE,同时还包含一些小工具,如javac,jar等;如果只要运行java程序,只需JRE即可
  • 三者之间关系:JDK>JRE>JVM

JVM知识点(一),jvm,java

(2)java程序运行
  • 编写好一个java程序后,使用javac进行编译后,会生成字节码文件(java程序中,使用的输出等模块是JRE里提供的基本类);
  • JVM采用栈架构,其指令由操作码(opcode)+操作数组成,操作码只有1个字节长度(0-255),指令数不能超过256条
  • 我们运行.class文件时,会启动一个JVM进程,JVM通过opcode+操作数来执行程序;
  • 执行方式有:解释执行:将opcode+操作数翻译为机器码文件;JIT:及时编译,在特定条件下,会将字节码编译为机器码文件之后才执行;

JVM知识点(一),jvm,java

2、JVM内存管理

(1)JVM内存布局
  • 堆中的数据是共享的,占用内存最大;
  • 执行字节码文件的模块为执行引擎,线程切换通过程序计数器进行;

JVM知识点(一),jvm,java

(2)虚拟机栈
  • 虚拟机栈是基于线程的,main方法启动后,以线程方式运行;
  • 线程的生命周期和栈的生命周期一样,栈中每一条数据都是栈帧;
  • 每调用一个java方法,就会创建一个栈帧并入栈,执行完改该方法后,便会出栈;
  • 所有栈帧出栈后,线程就会结束;

JVM知识点(一),jvm,java

(3)程序计数器
  • 程序计数器可以看作当前线程执行字节码的行号指示器,存储的是当前线程的进度;
  • 程序计数器有线程创建时产生,配合虚拟机站完成计算操作;

JVM知识点(一),jvm,java

(4)堆
  • 申请的对象都是存储在堆中,垃圾回收的对象是堆;
  • 堆在程序启动时创建,随着对象不断创建,堆空间会越来越小,就需要对堆内不常用的对象进行回收,即GC;
  • java对象分为基本数据类型和普通对象,基本数据类型存储在栈中;对于普通对象,JVM会在堆中创建对象,其他地方使用该应用;
  • 堆中的数据是线程共享的,栈中的数据是线程私有的;

JVM知识点(一),jvm,java

(5)元空间
  • Java8之前,类(创建对象的模板)都是存储在永久代中,且不会被JVM回收,随着类的增多,会造成JVM内存溢出;
  • java8中,元空间替代了永久代,在非堆上存储,JVM不会再出现方法区的内存溢出,但无限使用元空间会造成操作系统挂掉,可以通过-XX:MaxMetaspaceSize控制元空间大小;

JVM知识点(一),jvm,java

3、类加载

(1)类加载过程

JVM知识点(一),jvm,java

  1. 加载:将.class文件加载到JVM的方法区中;
  2. 验证:判断.class是否可以加载进入JVM中,不合法的直接抛出异常;低版本的JVM无法加载高版本类库;
  3. 准备:为类变量分配内存,并初始化值;此时实例对象还未分配内存,所以此时是在方法区中进行的;如下图,类变量有两次赋值,一次在准备阶段(赋予默认值),一次在初始化阶段(赋予程序员指定的值),而局部变量不会初始化赋值,没有默认值,不指定会报错;

JVM知识点(一),jvm,java

  1. 解析:将符号应用替换为直接应用。符号应用就是我们定义的字面量,如int a=1;a就是符号应用;直接应用是直接执行目标对象的指针或偏移量;
  2. 初始化:初始化成员变量;类信息只会存储一份到方法区中,类加载只会加载一次,所以static变量和staic代码块只会加载一次;JVM保证子类初始化之前,父类已经初始化;

如下代码,第一次new时执行顺序 :父类A的静态方法块---子类B的静态方法块--父类A的构造方法--子类B的构造方法;第二new时执行顺序 :父类A的构造方法--子类B的构造方法

JVM知识点(一),jvm,java

public class A {
    static {
        System.out.println("A static 代码块");//只会执行一次
    }
    public A(){
        System.out.println("A 构造方法");
    }
}

class B extends A{
    static {
        System.out.println("B static 代码块");//只会执行一次
    }
    public B(){
        System.out.println("B 构造方法");
    }

    public static void main(String[] args) {
        A a = new B();//第一次会调用静态代码块
        B b = new B();//不会调用静态代码块
    }
}

JVM知识点(一),jvm,java

(2)类加载器
  • 双亲委派机制:除了顶层的启动类加载器外,其余加载器都会在加载类时,先委托给父类加载,父类无法加载,才会交给子类去加载;
  • 双亲委派机制可以避免,我们自定义的类去覆盖java类库,保证java程序的安全;如下,我们使用写一个java.lang.String,运行main方法时,会报错,因为加载String类时是由父类加载器加载,加载的是java类库中的String,而java类库中的String类没有main方法,所以会报错;

JVM知识点(一),jvm,java

  • Tomcat中会破坏双亲委派机制,每一个web应用都有一个WebappClassLoader加载器,会加载应用程序类,只有当自己加载不到,才会委托给父类;使用该类加载器,JVM中出现同一个第三方库不同版本,应用程序去使用他不会发生冲突,他们由各自的类加载器加载,相互隔离(类对象是否相同由类加载器的命名空间和类对象本身决定,不同类加载器,命名空间不同);

JVM知识点(一),jvm,java

4、JVM异常处理

(1)异常表
  • 每一个方法都会携带一个异常表,表中每一行代表一个异常处理器,并且由from指针、to指针、target指针和异常处理类型组成;这些指针指向的是字节码的索引
  • from指针到to指针表示异常监控的范围(try中代码),target指针表示异常起始处理器的开始位置,即catch代码,如下图:异常监控范围为0-7(不包括7),异常处理器开始范围为10;

JVM知识点(一),jvm,java

(2)异常处理
  • 程序出现异常时,JVM会开始从上到下遍历异常表中每一行,当出现异常的字节码索引在异常表中的某一行的监控范围内,则会判断监控表的异常类型是否和当前程序抛出的异常类型一致,如果一致,会将字节码索引转到target指针;
  • 如果遍历完异常表的每一行,还是没有找到可以处理异常的异常处理器,则会将该方法的栈帧出栈,继续遍历调用该方法的调用者的异常表,重复异常表查找操作,最坏会遍历当前栈上所有方法的异常表;

JVM知识点(一),jvm,java

(3)finally块
  • finally代码块的内容会赋值到try和catch块中;
  • 异常表中会生成一行异常处理条目,监控整个try-catch,并且捕获异常;
  • 在执行完finally块后,若有捕获异常则会抛出异常;

5、OOM

(1)GC Root
  • 发生GC回收时,对于每一个对象,JVM总是回去寻找引用他的祖先,如果这个引用祖先已经挂了,就会将该对象回收,能够躲避垃圾回收的祖先,就是GC root;
  • 从GC root向下搜索,会产生一条Refrence Chain链,当任何一个对象不能和任何一个GC root产生关系时,就会被回收;
  • GC过程是找出活的对象,并认定其他对象为”无用”;

JVM知识点(一),jvm,java

GC Root有:

  • java线程中,当前正在被调用的方法的引用类型参数、局部变量、临时值等,即和栈帧相关的各种引用;
  • 所有被加载的java类;
  • java类的引用类型的静态变量;
  • 运行时常量池里的引用类型;(String或Class类型)
  • 用于同步的监控对象,如调用了对象的wait()方法;

大致分为三类:

  • 线程中的相关引用;
  • 类静态变量的引用;
  • JNI引用;
(2)引用类型
  • 强引用:当内存不足的时候,JVM会抛出OutOfMemoryError错误,即使程序终止,该对象也不会被回收;只有将GC Root与其断开,该对象才会被回收;如:我们平常new一个对象即为一个强引用,大量对象被创建且不能被回收时,会造成内存泄漏;
  • 软引用:维护一些可有可无的对象,只有当堆内存空间不够时,才会回收该对象,如果回收了软引用后,JVM内存空间任然不足,则会抛出OutOfMemoryError;软引用可以和引用队列(ReferenceQueue)联合使用,当软引用所引用的对象被回收后,就会加入到该队列中;
  • 弱引用:GC回收时,不管空间是否足够,都会回收弱引用所指向的对象;与WeakRefenece联合使用;
  • 虚引用:采用该引用的对象,在GC回收时,都会被回收;虚引用必须和引用队列联合使用。当GC回收对象时,若该对象有虚引用,则会将虚引用加入到引用队列中,在对象被回收之前,会执行一些逻辑;
(3)OOM原因
  • OOM可能发生的区域有堆、本地方法栈、虚拟机栈、元空间;
  • 内存容量过小,需要调整堆大小;
  • 错误的引用方式,发生内存泄漏。没有及时清理与GC Root相关联系,如线程池中的线程,复用时没有清理ThreadLocal,会造成内存泄漏;
  • 堆接口参数没有校验,传入参数超出范围;
  • 堆外内存无限制使用,会导致操作系统资源耗尽;

6、垃圾回收

(1)垃圾回收算法
  • 标记清除算法:根据GC Root遍历所有可达对象,进行标记,清除掉未标记的对象,该算法会造成内存碎片,如下,当GC回收后,剩余总空间还有7K,分配一个6K空间,无法分配成功

JVM知识点(一),jvm,java

  • 标记复制算法:会预留一半的空间,每次GC回收时,会将活对象复制到空闲的空间中去,所有算法中效率最高,但会造成空间浪费

JVM知识点(一),jvm,java

标记整理:GC回收时,会将存活对象整理到一起,不会造成空间浪费,同时也不会出现内存碎片,但效率最低;

JVM知识点(一),jvm,java

(2)年轻代
  • 存活时间比较短的对象都存储在年轻代中;
  • 使用的清除算法是标记复制算法,因为年轻代中存活的对象比较少,使用标记复制算法可以大大提高效率;
  • 年轻代分为伊甸园去(Eden)和两个幸存区(Survivor),对象会先分配到伊甸园区;
  • 当Eden区分配满时,会触发年轻代GC(Minor GC),过程如下:
    • Eden第一次GC,先将Eden区存活的对象移动到一个幸存区;
    • Eden再次GC,会采用标记复制算法,会将清理from区和Eden区,存活的对象被移动到To区,最后将From区清空即可;
  • Eden:From:To=8:1:1,会浪费10%的空间;-XX:SurvivorRatio可以设置(默认为8);
  • JVM会为每一个线程分配一个缓冲区(TLAB),TLAB在Eden区中,每个线程可以在TLAB上分配对象,避免锁竞争,但如果分配对象大小超过TLAB大小,则会在Eden区的公共区域分配;

JVM知识点(一),jvm,java

(3)老年代
  • 老年代一般采用标记清除、标记整理算法(Major GC),因为对象存活比较久,空间占用率比较大,拷贝不划算;
  • 对象进入老年代途径:
    • 提升:根据对象的年龄判断是否需要进入老年代,每发生一次Minor GC,存活下来的对象年龄会加1,直到年龄超过阈值,对象就会进入老年代;若对象不可达,会等到老年代发生GC时,才会被回收;阈值,可以通过参数 ‐XX:+MaxTenuringThreshold 进行配置,最大值是 15
    • 分配担保:年轻代中每次存活的对象都会进入Survivor区中,这个区域只有10%的大小,但我们无法保证每次存活对象的大小不超过10%,当Survivor空间不够时,就需要依赖老年代进行担保,直接在老年代中分配对象;
    • 大对象直接分配到老年代:当对象大小超过指定大小,会直接在老年代中分配对象;通过设置该参数 -XX:PretenureSizeThreshold,默认为0(即对象都在年轻代分配);
    • 动态对象年龄判断:当幸存区中相同年龄对象的大小的和超过幸存区的一半,幸存区中大于等于这个年龄的对象都会进入老年代;
(4)年轻代垃圾回收器
  • Serial 垃圾收集器:处理GC只有一个线程,GC时会停止一切用户进程,通常用于客户端应用,因为客户端应用通常不会建立过多对象;
  • ParNew垃圾收集器:处理GC时有多个线程,GC时同样会停止用户线程,存在线程切换开销,在单CPU下,性能比Serial差;追求降低用户停顿时间;
  • Paraller Scavenge垃圾收集器:也是多线程处理,追求CPU高吞吐量,能够在较短时间内完成指定任务;
(5)老年代垃圾收集器
  • Serial Old垃圾回收器:同样为单线程版本,采用标记整理算法,而年轻代中采用标记复制算法;
  • Parallel Old垃圾回收器:多线程处理,追求CPU高吞吐;
  • CMS垃圾回收器:以获取最短用户停顿时间为目标的垃圾收集器,采用用户线程和回收线程并发执行,用户不会感觉到明显的停顿;

7、CMS垃圾收集器

  • CMS全称并发标记清除垃圾回收器,在年轻代使用标记复制算法,在老年代使用标记清除算法,会造成内存碎片,只有通过full GC时,进行整理;
  • 主要是为了降低清除老年代时,避免长时停顿;
  • UseCMSCompactAtFullCollection:默认开启,表示Full GC时,需要整理内存,不能和用户进程并发,会造成停顿;

回收过程如下:

  1. 初始标记:只标记和GC Root直接相连的对象,因为GC Root在追踪活对象时最为耗时,同时还要标记与年轻代相关的对象;会发生STW;
  2. 并发标记:在初始标记阶段,进行并发标记(标记所有可达对象)。该阶段最为耗时,但可以和用户进程并行。在该阶段,可能会有对象引用发生变化,引用发生变化的对象会被标记为dirty,用于后续重新扫描;
  3. 并发预清理:并发预清理被标记为dirty状态的对象,将dirty状态对象重新标记,清除dirty状态,该阶段和用户进程并发,因此可能还会出现dirty状态;
  4. 并发可取消的预清理:重新标记阶段是需要STW(停止用户线程),因此在满足某些条件时,可以终止标记,避免会扫年轻代大量对象;
  5. 最终标记:第二次STW,标记所有存活对象。之前的并发预清理阶段可能发生多次,可能赶不上应用变化清况,所以最终标记会STW,停止应用进程,最终标记所有存活对象;
  6. 并发清除:与用户进程并发进行,清除所有不可达对象,可能会产生新的垃圾(浮动垃圾),该部分垃圾只能等到下一次GC时才能被回收;
  7. 并发重置:重置与CMS相关的内部结构,为下一次GC做准备;

JVM知识点(一),jvm,java文章来源地址https://www.toymoban.com/news/detail-679973.html

到了这里,关于JVM知识点(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JVM知识点汇总(2)

    目录 一. 垃圾回收的优点和原理. 并考虑两种回收机制 二. 垃圾回收器的基本原理是什么? 垃圾回收器可以马上回收内存吗? 有什么办法主动通知虚拟机进行垃圾回收? 三. Java 中会存在内存泄露嘛? 请简单描述 四.Ststem.gc() 和 Runtime.gc() 会做什么事情 五. finalize() 方法是什么时候

    2024年02月12日
    浏览(75)
  • 1、JVM相关知识点-类加载机制

    当我们用java命令运行某个类的main函数启动程序时,首先需要通过 类加载器 把主类加载到 JVM 通过Java命令执行代码的大体流程如下: 其中loadClass的类加载过程有如下几步: 加载 验证 准备 解析 初始化 使用 卸载 加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会

    2024年04月10日
    浏览(45)
  • Java知识点:

    1、Java中的(8个)基本数据类型及其包装类:         byte(字节)                  8          -128 - 127                                                                     0         short(短整型)             16         -32768 - 32768               

    2024年02月13日
    浏览(42)
  • java相关知识点

    1.String和StringBuffer如何互相转化 StringBuffer buffer = new StringBuffer(string); String string = buffer.toString();  2.如何实现两个数组内容的拷贝  3.如何去除字符串首尾空格 str.trim()  4.字符串和字符数组如何相互转换 字符串转字符数组:str.toCharArray(); 字符数组转字符串:strs.valueOf(char[] ch)  

    2023年04月23日
    浏览(45)
  • 脚踏Java知识点

    三元运算符和if语句格式的区别 语法格式: 表达式执行: 返回值: 使用场景: switch语句 switch语句的基本语法如下: switch语句的执行流程如下: 需要注意的是: 下面是一个示例,演示了如何使用 switch 语句判断星期几: 循环结构 for循环: 具体执行过程为 while循环: 具体

    2024年02月13日
    浏览(49)
  • JAVA知识点梳理

    boo=false; //每一轮排序都会将最大的一个排到最后 所以-i 2.第一次不同如果是数值,返回长度差 第一次不同如果是字符,返回两个字符的Ascrll码的差值

    2024年02月12日
    浏览(45)
  • Java 面试知识点

    基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的 语法,集合的语法,io 的语法,虚拟机方面的语法。 和都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为 true 时,整个运算结果才为 true,否

    2024年02月16日
    浏览(43)
  • Java 基础知识点

    Object 类相关方法   getClass 获取当前运行时对象的 Class 对象。 hashCode 返回对象的 hash 码。 clone 拷贝当前对象, 必须实现 Cloneable 接口。浅拷贝对基本类型进行值拷贝,对引用类型拷贝引用;深拷贝对基本类型进行值拷贝,对引用类型对象不但拷贝对象的引用还拷贝对象的相

    2024年02月13日
    浏览(64)
  • 常见java知识点1

    目录 1    什么是Spring框架?Spring框架有哪些主要模块? 2    使用Spring框架有什么好处? 3    Java常用的包(列举六个) 4    Arraylist 和 Linkedlist 的区别 5    HashMap和Hashtable的区别 6    Java中常见的 io 流? 7    说一下常见的几个线程池?(Java里面有4个线程池) 8  

    2024年02月16日
    浏览(39)
  • java面试常见知识点

    JDK(Java Development Kit)是Java开发工具包,是整个JAVA的核心,包括了Java运行环境JRE(Java Runtime Envirnment)、一堆Java工具(javac/java/jdb等)和Java基础的类库(即Java API 包括rt.jar)。 JRE是运行基于Java语言编写的程序所不可缺少的运行环境。JRE中包含了JVM,runtime class libraries和Jav

    2024年02月11日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包