0.JVM原理、java内存分析, 类加载器

目录

JVM原理

类加载器

内存分析

共享

1.堆内存(heap):

2.方法区【永久代/数据共享区/静态区】(method):JDK1.8取消

3.直接内存 

独有

1.栈内存(stack)

2.PC寄存器

回收机制

判断对象死亡

引用计数法

可达性分析算法

GC算法

垃圾收集器



JVM原理

https://blog.csdn.net/lzm1340458776/article/details/44153825

https://blog.csdn.net/SivanL/article/details/88865084

https://www.edrawsoft.cn/viewer/public/s/b6b85573046173


JVM生命周期:一个运行中的Java虚拟机有着一个清晰的任务:执行Java程序。你在同一台机器上运行三个程序,就会有三个运行中的Java虚拟机。

  1. 首先由类加载子系统加载.class文件, 把class文件的各部分加载到各个对应的数据区
    1. 每一个Java虚拟机都包含方法区(method area)和(heap),所有线程共享 从类文件中解析出来的信息保存与方法区中。程序执行时创建的对象都保存在中。
  2. Main()方法是程序的起点,程序中其他的线程都由他来启动。
    1. 当一个线程被创建时,会被分配只属于他自己的程序计数器“pc register”和Java(Java stack)。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。
  3. 栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个有关方法(Method)和运行期数据的数据集,当一个方法被调用时就产生了一个栈帧 ,并被压入到栈中
    1. 调用main方法产生main栈帧被压入到栈中, mian调用add方法又产生add栈帧被压入到栈中, add栈帧调用完出栈又回到main栈帧中
  4. 存储的就直接是对象的地址, 中会存放对象, 方法区存放对象类型数据/类的结构信息

 

JVM内存回收:Sun的JVMGenerationalCollecting(垃圾回收)原理是这样的:把对象分为年青代(Young)、年老代(Tenured)、持久代(Perm),对不同生命周期的对象使用不同的算法。(基于对对象生命周期分析)

垃圾回收器要回收对象的时候,首先要调用这个类的finalize()方法

通常我们说的JVM内存回收是在指堆内存回收,确实只有堆中的内容是动态申请分配的,所以以上对象的年轻代和年老代都是指的JVM的Heap空间,而持久代则是之前提到的MethodArea,不属于Heap。


类加载器

把class文件加载到jvm的工具

加载步骤:

  1. 加载     将类的class文件读入到内存,并为之创建一个java.lang.Class对象
  2. 链接    把类的二进制数据合并到JRE 1.验证,2.准备(分配内存设置初始值),3.解析(可选)
  3. 初始化    静态变量,静态代码块

加载时机:

  1. 创建类的实例,也就是new一个对象
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(Class.forName("com.lyj.load"))
  5. 初始化一个类的子类(会首先初始化子类的父类)
  6. JVM启动时标明的启动类,即文件名和类名相同的那个类    
     

双亲委派:都交给上一辈的人去加载

 


内存分析

jdk1.8 后没有了 永久代(方法区),用元空间(直接内存)来对方法区进行了实现,

直接内存不是元空间。

元空间是在虚拟机运行时区域内的。

而直接内存不在运行时区域。直接内存占用的是本地内存,不再占用虚拟机内存。


共享

 Java虚拟机加载并解析一个类以后,将从类文件中解析出来的信息保存与方法区中。程序执行时创建的对象都保存在中。

1.堆内存(heap):

存储对象数据,提供所有类实例(new出的对象)和数组对象存储区域。堆中不存放基本类型和对象引用,只存放对象本身,jvm只有一个堆区,对象被使用完不会马上消失,等待垃圾回收器不定时回收。

2.方法区【永久代/数据共享区/静态区】(method):JDK1.8取消

方法区包含所有的class信息static变量、常量池。方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。运行时常量池都分配在 Java 虚拟机的方法区之中。随着JVM的开启而产生,在JVM结束时而消失的,生命周期和jvm一致。

   2.1 常量池:

   常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值(final)还包含一些以文本形式出现的符号引用,比如:

  1. 类和接口的全限定名
  2. 字段的名称和描述符
  3. 方法和名称和描述符

如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。

3.直接内存 (DirectMemory)

直接内存占用的是本地内存,不再占用虚拟机内存。

通过jmap无法查看该快内存的使用情况。只能通过top来看它的内存使用情况。

  • DirectMemoryArea 并不等于物理内存,在jre\lib\rt.jar!\sun\misc\VM 类限制
  1. 默认 directMemory = 64 * 1024 * 1024; //64M
  2. JVM参数 -XX:MaxDirectMemorySize 会在启动JVM的时候以sun.nio.MaxDirectMemorySize加载进参数中
  3. sun.nio.MaxDirectMemorySize = -1, 则等于JVM运行时最大内存 directMemory = Runtime.getRuntime().maxMemory();  
  4. sun.nio.MaxDirectMemorySize != -1, 则 directMemory = sun.nio.MaxDirectMemorySize

堆外内存也是由GC负责回收的

当Direct ByteBuffer分配的堆外内存到达指定大小后,即触发Full GC


独有

PC寄存器中保存线程执行的下一条指令。Java保存了一个线程调用方法时的状态,包括本地变量、调用方法的参数、返回值、处理的中间变量。

1.栈内存(stack)

每个线程都有自己独有的一个栈区,出了作用域马上消失释放空间。

存储局部变量,只保存基础数据类型的对象和自定义对象的引用,基本数据类型(char,byte,short,int,long,float,double,boolean)的变量、引用数据类型(reference类型)

2.PC寄存器


创建字符串内存分析:

字符串常量池属于方法区中的一块。

String str = "hello"    这个语句会先检查字符串常量池是否存放这个"hello"这个字符串对象,如果没有存在,那么就会在字符串常量池中创建这个字符串对象,如果存在直接返回该字符串的内存地址值。
String str = new String("hello")     该语句会创建两个对象:首先会先检查字符串常量池中存不存在"hello"这个字符串对象,如果不存在就会创建,如果存在就不会创建。然后new String这个语句就会在堆内存中开辟一个字符串对象,把字符串常量池中的"hello"字符串内容拷贝至堆内存中的字符串对象,然后返回堆内存中字符串对象的内存地址总共两个对象
 


回收机制

年轻代(Young Generation) 年老代(Old Generation) 永久代(Permanent Generation)

年轻代包括(一个Eden区,两个Survivor区:对称没先后关系一个称为(From sqace),一个称为(To Space)。)

  1. 每次使用Eden和其中一块Survivor
  2. 大部分对象在Eden区中生成,
  3. Eden内存不够,就进行Minor GC 清理年轻代
  4. 每次回收时,将Eden和Survivor中还存活着的对象一次性复制到另外一块Survivor空间,对象的年龄加 1
  5. 重复多次(最多15次,一般回收器默认15次 CMS默认 6,通过参数 -XX:MaxTenuringThreshold 来设置)年龄增加到MaxTenuringThreshold岁时 对象晋身,Survivor中没有被清理的对象就会复制到老年区(Old)
  6. Survivor对象占用空间大于50%也会对象晋身, 复制到老年区(Old)

年老代 在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此年老代中存放的都是一些生命周期较长的对象

永久代 存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,jdk1.8变为元空间,使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。

 

GC有两种类型:Scavenge GC和Full GC。

//清理年轻代
新生代收集(Minor GC / Young GC)(Scavenge GC)  
新对象生成,并且在年轻代的Eden区申请空间失败时,就会触发Scavenge GC对Eden区域进行GC,清除非存活对象。不会影响到年老代

//清理老年代
老年代收集(Major GC / Old GC)   

//对整个新生代和部分老年代进行垃圾收集。
混合收集(Mixed GC)

//收集整个 Java 堆和方法区。
整堆收集 (Full GC)    //成本较高,会对系统性能产生影响。
对整个堆进行整理,比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:
· 年老代(Tenured)被写满
· 永久代(Perm)被写满 
· System.gc()被显示调用 
· 上一次GC之后Heap的各域分配策略动态变化

Full GC会触发 STW(stop the word) 程序停止工作

判断对象死亡

对堆垃圾回收前的第一步就是要判断哪些对象已经死亡

引用计数法

给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。

可达性分析算法

这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。

可作为 GC Roots 的对象包括下面几种:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 本地方法栈(Native 方法)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 所有被同步锁持有的对象

GC算法

  • 标记-清除
  • 复制
  • 标记-整理
  • 分代收集 (新生代用复制,老年代用标记-清除/整理)

垃圾收集器

串行收集器

并行收集器

并发收集器

指用户线程与垃圾收集线程同时执行(但不一定是并行,可能会交替执行),用户程序在继续运行,而垃圾收集器运行在另一个 CPU 上。

具体实现:

 新一代垃圾回收器  ZGC 收集器

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xyc1211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值