JVM那些事儿,这些必会的知识点(一)

我是方圆,一边做课程设计,一边更博客

在这里插入图片描述

在这里插入图片描述

1. native关键字(了解)

Java无法直接访问系统的底层(我们熟悉的启动线程中start0()方法就是被native关键字修饰),所以,引入native关键字对Java实现扩展,通过JNI(Java Native Interface)接口调用其他语言(如C和C++),实现对底层的访问。

2. 方法区

方法区中存在静态变量常量类信息运行时常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关。(static,final,Class,常量池)

3. 栈

栈中保存的是八大基本类型,对象的引用

4. 类的加载过程

类的生命周期如下图所示

在这里插入图片描述

  1. 加载
    在加载阶段,我们可以参考java.lang.ClassLoader中的loadClass()方法,会在方法区中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
  2. 验证
    这个阶段主要确保了Class文件的字节流中包含的信息符合虚拟机的要求,并且不会危害虚拟机的安全。验证是非常重要的,但是不是必须的,它对程序运行期没有影响
  3. 准备
    准备阶段是正式为类变量(static)分配内存并设置初始值的阶段,这些变量所使用的内存都在方法区中分配。
  4. 解析
    解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
  5. 初始化
    这个阶段是类加载过程的最后一步。在准备阶段,我们已经对类变量赋值过(初始0值),而在这个初始化阶段,则根据我们写的代码去初始化变量和其他资源。或者说,初始化阶段是执行类构造器<clinit>()方法的过程。<clinit>()方法是由编译器自动收集类中所有的类变量赋值语句静态代码块中的赋值语句所产生的。
    虚拟机会保证一个类的类构造器<clinit>()在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的类构造器<clinit>(),其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。需要特别注意的是,其他线程虽然会被阻塞,但是当<clinit>()执行完毕,其他线程被唤醒之后不会再去执行这个方法,因为在同一个类加载器下,一个类型只会被初始化一次

5. 对象的实例化过程

实例化一个类的对象,是一个典型的递归过程,如下图所示。

在这里插入图片描述

在准备实例化一个对象前,首先会准备实例化该类的父类,如果该类的父类还有父类,那么还会向上实例化,直到递归到Object类。实例化Object类之后,再依次向下对各类进行实例化。

5.1 举一个小例子

Student student = new Student();
  1. 当虚拟机遇到一条new命令的时候,先检查方法区中是否存在该对象的类信息,并且检查该类是否被加载、解析和初始化
    (1)如果方法区中没有该类的信息,那么会抛处ClassNotFoundException
    (2)若以上的检查全部通过,则进入下一步工作
  2. 类加载完成后,虚拟机将为新生对象分配内存
  3. 从堆中划分一块儿相应大小的内存给新的对象
  4. 为新对象的成员变量附上初始值
  5. 设置并保存对象头信息(Object Header),对象头信息包括该对象是哪个类实例、对象的哈希码、对象的 GC 分代年龄等信息
  6. 一般来说,执行 new 指令之后会接着执行 方法,把对象按照程序员规定的构造函数进行初始化

6. 堆

在这里插入图片描述
中存放的是对象,包含年轻代和老年代,永久代(在jdk1.8被元空间取代)中包含方法区

6.1 分代概念

新生成的对象首先放到年轻代Eden区,当Eden空间满了,触发Minor GC,存活下来的对象移动到Survivor0区,Eden再次填满后触发执行Minor GC,Survivor0区存活对象移动到Suvivor1区,这样保证了一段时间内总有一个survivor区为空(空的为to,非空的为from)。经过15次Minor GC仍然存活的对象移动到老年代。
老年代存储长期存活的对象,占满时会触发Major GC=Full GC,GC期间会停止所有线程等待GC完成,所以对响应要求高的应用尽量减少发生Major GC,避免响应超时。

  • Minor GC : 清理年轻代
  • Major GC : 清理老年代
  • Full GC : 清理整个堆空间,包括年轻代和永久代
    在这里插入图片描述

6.2 为什么分代?

将对象根据存活概率进行分类,对存活时间长的对象,放到固定区,从而减少扫描垃圾时间及GC频率。针对分类进行不同的垃圾回收算法,对算法扬长避短。

6.3 为什么survivor分为两块相等大小的幸存空间?

主要为了解决碎片化。如果内存碎片化严重,也就是两个对象占用不连续的内存,已有的连续内存不够新对象存放,就会触发GC。

6.4 JVM堆内存中常用参数

参数 描述
-Xms 堆内存初始化大小
-Xmx 堆内存最大允许大小,一般不超过物理内存的80%
-XX:+PrintGCDetail 打印GC详细信息
-XX:PermSize 非堆内存初始大小,一般设置200M
-XX:MaxPermSize 非堆内存允许的最大大小
-Xns 年轻代内存初始化大小
-Xmn 年轻代内存允许的最大内存大小
-XX:SurvivorRatio=8 Eden区与Survivor区的容量比值,默认为8
-Xss 栈内存大小

在这里插入图片描述

同系列

JVM那些事儿,GC(二)

参考文献

JVM类生命周期概述:加载时机与加载过程

深入理解Java对象的创建过程:类的初始化与实例化

JVM 虚拟机与对象创建过程

Java堆内存又溢出了!教你一招必杀技

【狂神说Java】JVM快速入门篇

原创文章 56 获赞 19 访问量 6017

猜你喜欢

转载自blog.csdn.net/qq_46225886/article/details/106046585