JVM基础

本文详细介绍了Java虚拟机的内存结构,包括类加载机制、运行时数据区、垃圾回收以及各种内存区域的作用。类加载涉及装载、链接和初始化三个阶段,其中双亲委派模型保证了类加载的有序性。方法区用于存储类信息,堆是对象实例的存储区域,虚拟机栈则保存线程的执行状态。此外,文章还讨论了垃圾回收的判断标准和算法,以及常见的JVM参数和工具的使用。
摘要由CSDN通过智能技术生成

类加载机制

在这里插入图片描述

1. 装载

1. 通过全限定类名找到该类
2. 在方法区中保存该类的描述信息
3. 在Java堆中生成一个代表这个类的java.lang.Class对象

类加载器

类加载器名称说明
Bootstrap ClassLoader加载$JAVA_HOME中jre/lib/rt.jar里所有的class或Xbootclassoath选项指定的jar包
Extension ClassLoader加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar 或 -Djava.ext.dirs指定目录下的类和jar包
App ClassLoader加载classpath中指定的jar包及Djava.class.path所指定目录下的类和jar包
Custom ClassLoader通过java.lang.ClassLoader的子类自定义加载class,属于应用程序根据自身需要自定义的ClassLoader,如Tomcat、jboss都会根据j2ee规范自行实现ClassLoader

双亲委派机制

定义:如果一个类加载器在接到加载类的请求时,它首先不会自己尝试去加载这个类,而是把这个请求任务委托给父类加载器去完成,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

优势:Java类随着加载它的类加载器一起具备了一种带有优先级的层次关系。比如,Java中的Object类,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object在各种类加载环境中都是同一个类。如果不采用双亲委派模型,那么由各个类加载器自己取加载的话,那么系统中会存在多种不同的Object类。

如何打破双亲委派机制

实现一个自定义的类加载器,重写loadClass方法。

2. 链接

1. 验证:保证被加载类的正确性,包括文件格式验证、元数据验证、字节码验证、符号引用验证
2. 准备:为类的静态变量分配内存,并将其初始化为默认值
3. 解析:把类中的符号引用转换为直接引用

3. 初始化

对类的静态变量,静态代码块执行初始化操作。

运行时数据区

在这里插入图片描述

1. 方法区

(1)方法区是各个线程共享的内存区域,在虚拟机启动时创建
(2)用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
(3)虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却又一个别名叫做Non-Heap(非
堆),目的是与Java堆区分开来
(4)当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常
(5)方法区在JDK 8中就是Metaspace【元空间】,在JDK6或7中就是Perm Space【永久代】
(6)Run-Time Constant Pool在方法区分配

2. 堆

(1)堆是Java虚拟机所管理内存中最大的一块,在虚拟机启动时创建,被所有线程共享
(2)Java对象实例以及数组都在堆上分配
(3)当堆无法满足内存分配需求时,将抛出OutOfMemoryError异常

3. Java虚拟机栈

(1)虚拟机栈是一个线程执行的区域,保存着一个线程中方法的调用状态。换句话说,一个Java线程的运行状态,由一个虚拟机栈来保存,所以虚拟机栈肯定是线程私有的,独有的,随着线程的创建而创建
(2)每一个被线程执行的方法,为该栈中的栈帧,即每个方法的执行对应一个栈帧
(3)调用一个方法,就会向栈中压入一个栈帧;一个方法调用完成,就会把该栈帧从栈中弹出
在这里插入图片描述

栈帧

每个栈帧对应一个被调用的方法,可以理解为一个方法的运行空间。
每个栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、动态链接、方法返回地址(Return Address)和附加信息。

4. 程序计数器

一个JVM进程中有多个线程在执行,而线程中的内容是否能够拥有执行权,是根据CPU调度来的。假如线程A正在执行到某个地方,突然失去了CPU的执行权,切换到线程B了,然后当线程A再获得CPU执行权的时候,怎么能继续执行呢?这就是需要在线程中维护一个变量,记录线程执行到的位置。
(1)程序计数器占用的内存空间很小,由于Java虚拟机的多线程是通过线程轮流切换,并分配处理器执行时间的方式来实现的,在任意时刻,一个处理器只会执行一条线程中的指令。因此,为了线程切换后能够恢复到正确的执行位置,每条线程需要有一个独立的程序计数器(线程私有)
(2)如果线程正在执行Java方法,则计数器记录的是正在执行的虚拟机字节码指令的地址
(3)如果正在执行的是Native方法,则这个计数器为空。

5. 本地方法栈

如果当前线程执行的方法是Native类型的,这些方法就会在本地方法栈中执行。

JVM内存模型

在这里插入图片描述
**Young区:**年轻代,分为两大块,一块是Eden区,一个是Survivor区(S0+S1)。内存占比:Eden:S0:S1=8:1:1
**Eden区:**新生代,正常对象创建所在区域,大多数对象“朝生夕死”
**Survivor区:**幸存区,分为两块S0和S1,也可以叫做From和To,在同一个时间点上,S0和S1只能有一个区有数据,另外一个是空的。

**Old区:**老年代,都是年龄比较大的对象,或者超过了某个阈值的对象。

垃圾回收

如何确定一个对象是垃圾

(1)引用计数法:一旦相互持有引用,就导致对象永远没法被回收

(2)可达性分析:由GC Root出发,开始寻找,看看某个对象是否可达
​ GC Root:类加载器、Thread、本地变量表、static成员、常用引用、本地方法栈中的变量等

垃圾回收算法

(1)标记清除:遍历内存中的对象,对需要回收的对象进行标记,标记完成后直接回收,会导致内存地址不连续
(2)复制:将一块完整的区域一分为二,把需要回收的对象复制到另外一块空的区域,两个区域始终有一个是空的。回收后内存地址连续,但是浪费了一部分内存作为临时空间。
(3)标记整理:标记要回收的对象,移动到有空缺的位置上,回收后内存地址连续,但是整理过程较慢

分代收集算法

Young区:复制算法
Old区:标记清除或标记整理

垃圾回收器

在这里插入图片描述

如何开启收集器

在这里插入图片描述

JVM参数

(1)标准参数
(2)-X参数
(3)-XX参数
​ -XX:[+/-] -XX:+UseG1GC
​ -XX:= -XX:InitialHeapSize=100M
(4)其他参数
​ -Xms100M ===>-XX:InitialHeapSize=100M
​ -Xmx100M -Xss100

JVM命令

(1)jps: 当前的java进程
(2)jinfo: 查看某个java进程目前的参数设置的情况
(3)jstat: 查看java进程统计性能
(4)jstack:查看当前java进程的堆栈信息
(5)jmap:打印出堆转存储快照 jmap -heap PID
​ dump出堆内存相关信息:jmap -dump:format=b,file=heap.hprof PID

常用工具

(1)jconsole
(2)jvisualvm
(3)arthas
(4)mat/perfma:内存相关的信息
(5)gceasy.io/gcviewer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值