当前位置: 首页 > article >正文

JavaPro _JVM 知识点速记 JVM大全

JVM

Java Virtual Machine,Java虚拟机

        为什么叫Java虚拟机  .顾名思义,我们编写java代码,编译java代码,不是让他在windows上跑,在linux上跑,或者是在MacOS上跑,   而是在Jvm上跑

jvm作用

jvm负责把编译后的字节码转换为机器码

JVM的组织架构

1.类加载器(ClassLoader)

2.运行时数据区(RuntimeData Area)

3.执行引擎(Execution Engine)

4.本地库接口(Native Interface

     

jvm内部构造

  1. 类加载部分 : 负责把硬盘上字节码加载到内存中(运行时数据区)

  2. 运行时数据区: 负责存储运行时产生的各种数据 类信息,对象信息,方法信息...

  3. 执行引擎: 负责将字节码转为机器码

4.本地方法接口: 

5垃圾回收部分

类加载


类加载系统,负责将硬盘上的字节码文件加载到jvm中,生成类的Class对象,存储在方法区.

类就是一个模版

类加载过程

类从被加载到 JVM,分别是加载、验证、准备、解析、初始化。其中验证、准备和解析这三个阶段统称为连接。

1.加载

以二进制文件流进行读取

在内存中为类生成Class对象

2.链接

验证 验证字节码的结构是否正确

准备 为类的静态属性进行初始化赋值

        static int a =  123; 在准备阶段a的值为0,在初始化阶段才赋值为123  

解析 :将类中的符号引用替换成直接引用(符号引用是Class 文件的逻辑符号,直接引用指向的方法区中某一个地址).

3.初始化

初始化阶段主要是为类中静态成员进行赋值.

因为类加载执行完初始化阶段,才说明类加载完成了.

类在哪些情况下会被加载   (类只要被用到了就会被加载)

1调用类中静态成员(变量,方法)    类名,静态变量   类名,静态方法

2new 类的对象

3在类中执行main()

4反射加载类 Class.forName("地址")

5子类被加载

类在以下两种情况下,是不会被加载的

1. 类作为数组类型
Demo demo[] = new Demo[10]; //new 的数组对象   不是Demo对象
​
2.只是访问类中的静态的常量
System.out.println(Demo.P);//优化   不加载整个类了,  只获取到用到的静态常量

类加载器   (加载类的实现者)

类加载器就是实际负责读取类的功能.

类加载器分类:  

1 按照java语言分类,分为 用java语言实现的和非java语言实现

1>引导类加载器

不是用java语言写的使用c/c++实现的,用来加载java系统的中的类 如String

2>扩展类加载器

此加载器,由java语言实现,用于加载java8\jre\lib\ext下的类

3>应用程序加载器

此加载器,由java语言实现,用于加载我们自己开发的程序类

双亲委派机制

当加载一个类时,先让上级的类加载器去加载,优先加载系统中的类,直到启动类加载器,

如果到达顶部的类加载,找到了,那就返回, 如果顶部类加载器找不到,再次向下委派,让子类加载器去加载.

在哪一级找到了,就返回即可,如果向下,都没有找到,那么就抛出类找不到异常.

为什么使用双亲委派机制

优先加载系统中的类,防止我们的类替换了系统中的类.

如何打破双亲委派机制

在 ClassLoader 类中涉及类加载的方法有两个,loadClass(String name), findClass(String name),这两个方法并没有被 final 修饰,也就表示其他子类可以重写. 重写 findClass 方法

我们可以通过自定义类加载重写方法打破双亲委派机制,

运行时数据区------方法区,堆,栈,虚拟机栈,本地方法栈,程序计数器


 

运行时数据区就是用来存储各种运行时产生的数据

程序计数器


        程序计数器,用来记录每个线程执行指令的位置,因为cpu要切换执行

特点:

1程序计数器占用内存小,速度快,

2线程私有,每一个线程都有一个程序计数器用来记录每个线程执行到的位置.

3声明周期与线程相同

4此区域不存在内存溢出的问题

虚拟机栈


虚拟机栈是运行的结构,主要用来执行java语言写的方法

栈是私有的,每个线程运行,都有一个自己的栈空间

栈运行的特点,先进后出/

栈空间是会有内存溢出的(比如说递归调用时)

一个方法被调用后,在栈中称为一个栈帧,保存局部变量,操作数栈,方法返回地址

本地方法栈


本地方法栈中,主要用来运行调用本地方法(native关键字修饰的方法,是由操作系统实现的方法,C/C++)

堆      ---java产生的对象都存储在堆中


 特点:

堆空间是虚拟机中空间最大的一块区域,而且大小可以调整,(运行时数据区,除了程序计数器区域不能调整大小,其他四个区域都可以调整大小)

堆空间被所有线程共享

堆空间是会出现内存溢出

堆空间是垃圾回收的重点区域

堆空间区分

1>年轻代/新生代

       1. 伊甸园区(刚new出来的对象存储)

        2.幸存者0 区(from)  3.幸存者1区(to)

2>老年代/老年区

为什么分区

        可以把存活时间不同的对象放在不同的区域,对不同的区域进行垃圾回收

频繁的回收新生代,较少的回收老年代   减少及 GC  频率。

对象在堆空间的分配过程

        新创建的对象都存储在伊甸园区当垃圾回收时 把伊甸园区的对象转移到幸存者0区,当后面继续回收垃圾时,再把伊甸园区和幸存者0区存活的对象转移到幸存者1区

每次保证有一个幸存者区是空闲的,减少内存碎片 当一个对象经过15次垃圾回收时,依然存活,则就将此对象移动到老年区

还有当一个对象比较大,也可以直接放入老年区

补充:对象的回收的次数最大上线时15 在对象头有4bit位来记录回收次数,

可以通过-XX:MaxTenuringThreshold=<N> 设置次数

设置堆空间的参数

-Xms:初始堆空间内存

-Xmx:最大堆空间内存

-Xmn:设置新生代的大小

-XX:MaxTenuringTreshold:设置新生代垃圾的最大年龄

具体的可以参考官方文档, 这些参数的设置,也是jvm调优的一部分 根据实际使用情况,设置各参数的大小

方法区


特点:

方法区主要存储加载到内存中的类信息,

 方法区物理上与堆属于一个空间,但是在一般逻辑上进行区分,称为元空间(方法区)

方法区的大小可以设置 通过-XX:MetaspaceSize=<N>参数方法区的空间一但空间不足,就会触发 FULL GC (整堆收集) ,会影响应用程序的线程 一般情况下,可以把方法区设置较大一些

方法区会出现内存溢出的

方法区存在垃圾回收的

方法区什么时候会被卸载  (方法区的垃圾回收)

1 该类以及该类的子类的所有的对象都不存在了

2 加载该类的加载器不存在了

3 加载该类的CLass对象不存在了

由于条件比较苛刻 一般情况下 可以认为类被加载后就不再释放

本地方法接口


用于对接调用的本地方法

什么是本地方法,

被native修饰的方法就是本地方法

 // new Object().hashCode(); //public native int hashCode();  获取内存地址
 // new FileInputStream("").read();  private native int read0() 读硬盘数据
 //new Thread().start();//    private native void start0(); 把线程注册到操作系统

为什么要使用本地方法

java属于应用程序语言,没有权限直接操作硬件设备 使用本地方法与硬件进行交互

例如获取内存地址,读取硬盘数据,这是就需要对操作系统中的本地方法进行实现

执行引擎


1作用

可以将字节码指令解释/编译成机器码指令

注意提到了两次编译,

第一次编译是指将.java文件编译为.class文件.  属于开发阶段,与运行无关,也可以成为前端编译

第二次变异是在运行时,在虚拟机中通过执行引擎将字节码编译为机器码,称为后端编译

2解释器和编译器

解释器采用不编译,是逐行解释的方式,无需编译,可以实现立即的解释执行的操作,但是效率略低

编译器将字节码整体编译后执行,编译需要花费一段时间,但是编译后的执行效率高

3为什么这样设计  ---Java 是半编译半解释型语言

解释执行,虽然效率低,但是在程序开始后,可以立即投入使用

编译执行,虽然将一些热点代码编译后缓存起来执行那个效率高,但是花费时间

所以采用开始时,使用解释器执行,等编译完成后采用编译执行

垃圾回收


相关概念

1.什么是垃圾

在运行过程中,如果一个对象没有被任何引用所指向,那么这个对象就称为垃圾对象.

如果垃圾对象不清理,后来的对象就可能没有地方存放,进而导致内存溢出.

2.早期和现代的垃圾回收

早期c/c++都是手动的申请和释放内存,

好处: 可以精确的管理内存, 用的时候申请,不用的时候可以立即释放.

缺点: 给程序员带来负担, 一旦忘记释放,一直存在.

现代java,python,c#....语言都采用自动垃圾回收

好处: 解放了程序员,不需要关心什么时候释放垃圾对象

缺点: 降低了程序员对内存管理的能力

3.垃圾回收的区域

运行时数据区中的堆和方法区是回收的区域

堆是回收的重点区域

频繁的回收新生代

较少回收老年代

基本不回收方法区(在full gc 整堆回收时 ,才会对方法区进行回收)

4.内存溢出与内存泄漏

内存溢出: 内存不够用了,还继续有新的对象产生,此时回报出内存异常错误.

内存泄漏: 一些对象已经不再使用了,但是垃圾收器,却不能回收它,例如数据库连接对象,网络SOcket对象,

还有IO流对象等,提供close()进行断开,但是没有调用,那么垃圾回收器认为此对象还在使用中.这些对象就悄悄的占用着内存资源, 这种现象称为内存泄漏, 久而久之会引发内存溢出问题.

垃圾回收相关算法


标记阶段算法

标记阶段主要就判断哪些对象是垃圾对象,把没有引用指向的对象标记出来,

在垃圾回收时进行回收.

标记阶段提到两种算法:

1.引用计数算法(目前没有被使用)

在对象中提供一个计数器,有引用指向对象,那么计数器就加一,当计数器为0时,表名该对象没有被任何引用指向.

实现简单,

但是有一个致命的缺陷,无法解决循环引用问题.

多个对象之间相互引用,计数器不为0 ,但是此时这几个对象,与外界已经没有联系了,使用不到了,

他们相互又有引用,垃圾收集器不能回收他们,会造成内存泄漏问题.

2.可达性分析算法

可达性分析算法,可以解决引用计数算法中的循环引用问题.

思想是从一些活跃对象开始查找,与活跃对象相连接的对象,都是被使用的,

如果没有与与这些活跃对象相连接的对象,就可以判断为垃圾对象

哪些对象可以称为活跃对象(GCRoots):

1.虚拟机栈中引用的对象 比如:各个线程被调用的方法中使用到的参数、局部变量等。

2.方法区中类静态属性引用的对象,比如:Java 类的引用类型静态变量

3.所有被同步锁 synchronized 持有的对象 4.Java 虚拟机内部的引用。 基 本 数 据 类 型 对 应 的 Class 对 象 , 一 些 常 驻的异常对象(如:NullPointerException、OutofMemoryError),系统类加载器。

finalize机制

java中的Object类中提供一个finalize()方法,

这个方法在对象被判定为垃圾后,在垃圾回收器回收这个对象时,会调用finalize().

finalize()规定只会被调用一次. 假如一个对象第一被回收时,调用finalize(),在finalize()中又被使用到了,复活了,

当第二次再被判定为垃圾,回收时,不再调用finalize()方法.

我们可以重写finalize(),去执行一些对象回收前的最终操作, 但是我们不要自己主动的去调用该方法.

还有此方法中内容需要慎重,否则,影响垃圾收集器的性能.

由于finalize()存在

把对象分为三种状态:

可触及: 使用中的对象,没有被判定为垃圾

可复活: 被判定位垃圾对象,但是还没有调用finalize(),有可能在finalize()中复活.

不可触及: 第二次被判定为垃圾,finalize()已经调用过了.

回收阶段算法

标记-复制算法:

可以有多块内存,每次回收时,将正在使用的内存块中的存活对象,复制到另一块未使用的内存块中,

然后清除原来的内存块.

特点: 使用到多块内存

回收时需要移动对象,适合内存小,存活对象少,垃圾对象多的场景 (适合新生代)

回收后,内存碎片少.

标记-清除算法:

只需要一块内存空间,把存活对象是不用进行移动,直接清除垃圾对象.

特点: 只需要一块内存,

不需要移动存活对象, 只清除垃圾对象

回收后,会产生内存碎片问题

适合于存活对象多且对象较大的场景 (适合老年代)

标记-压缩算法

标记压缩算法,就是在标记清除算法的基础上,对存回对象进行移动,将存回对象移动到内存一端,按顺序排放,

清除边界之外的区域,

是一种移动式的垃圾回收算法,回收后,内存中没有内存碎片

适合老年代回收

分代收集

年轻代的对象,存活的少,垃圾多,使用标记复制算法进行回收

老年代的对象大,且存活时间长,采用标记清除和标记压缩算法进行回收.

优化垃圾回收效率

垃圾收集器


垃圾收集器是jvm中垃圾回收的实现者.

垃圾收集器种类很多,不同的虚拟机中可以使用不同的垃圾收集器.

垃圾收集器分类:

从垃圾收集器线程数量上划分:

单线程, 垃圾收集器中,只有一个线程在执行垃圾回收操作

多线程,垃圾收集器中,有多个线程在执行垃圾回收操作

从工作模式上分为:

独占式的: 当垃圾回收线程在执行时,其他用户线程暂停(把用户线程暂停的现象称为stw)

并发式的: 垃圾回收线程和用户线程可以同时执行, 减少了用户线程暂停的次数和时间

按照工作内存分为:

新生代垃圾收集器

老年代垃圾收集器

jdk8支持一下几种垃圾回收器

CMS(Concurrent Mark Sweep) 并发标记清除

首创了垃圾回收线程和用户线程并发执行, 追求低延时.

初始标记阶段 : 独占式的

并发标记阶段: 并发式的

重新标记阶段: 独占式的

并发清除阶段: 并发式的

G1(Garbage-First)收集器

首先G1垃圾收集器,适用于整堆收集,不再区分新生代,老年代

适用于服务器场景.

G1垃圾收集器,将每个区域再划分为更小的区域, 例如将伊甸园区划分为多个小区域,

优先回收垃圾对象较多的区域, 故得名Garbage-First--垃圾优先.

也是支持并发执行

查看并设置垃圾收集器

-XX:+PrintCommandLineFlags -version 打印jdk中使用的垃圾收集器信息

-XX:+UseG1GC 设置垃圾收集版本


http://www.kler.cn/a/567435.html

相关文章:

  • 【AVL树】—— 我与C++的不解之缘(二十三)
  • GitCode 助力 python-office:开启 Python 自动化办公新生态
  • 机器学习的通用工作流程
  • 若依框架修改为多租户
  • OptiTrack光学跟踪系统:引领工厂机器人应用的革新浪潮
  • 克隆项目到本地
  • C++(Qt)软件调试---Linux 性能分析器perf(29)
  • lua学习(二)
  • Compose笔记(七)--Modifier
  • rustup-init.exe 安装缓慢的解决办法
  • CSS浮动详解
  • [密码学实战]Java实现SM2数字信封(结合SM4对称加密)生成与解析
  • 前后端常用正则
  • Ruby基础
  • QT异步编程之QtConcurrent
  • C语言(17)------------>数组的练习
  • 【练习】【贪心】力扣134. 加油站
  • 人工智能销售客服app开发,OpenAI宣布GPT-5免费使用?Deepseek让AI巨头全跪了
  • 【 实战案例篇三】【某金融信息系统项目管理案例分析】
  • leetcode28 找出字符串第一个匹配值的下标 KMP算法