JVM内存、类加载学习记录

1 篇文章 0 订阅
本文详细探讨了JVM内存的各个主要部分,包括堆、方法区、JVM栈、本地方法栈和PC计数器。重点讨论了内存溢出的情况,如堆溢出、栈溢出、无法创建新线程等问题。此外,文章还涵盖了GC(垃圾回收)的工作原理,如可达性分析算法、四种引用类型,并分析了不同类型的内存回收算法和收集器。最后,讲解了对象分配策略,尤其是Minor GC,并深入介绍了类加载的过程,包括加载、验证、准备、解析和初始化五个步骤,以及类加载器的双亲委派模型。
摘要由CSDN通过智能技术生成

Jvm内存分为哪几个主要部分?

  • 主要用来存储实例对象,现今HotSpot虚拟机将该区做了分代分区处理,分为Eden,2个Supervisor,和Old

  • 方法区

    存储编译后产生的信息,如class,静态变量和常量等,也可以叫Non-heap,或者在HotSpot虚拟机中称为永久代

  • jvm栈

    用来执行方法

  • 本地方法栈

    执行本地方法

  • PC计数器

有哪些原因会产生内存溢出?

Java Heap Space堆溢出

内存泄漏leak

查看GCRoots引用链,可能是有对象一直持有不该持有的引用导致未被正常回收

内存溢出overflow

如果不存在leak说明对象都是必须存在的,但是内存确实不够,想办法优化代码结构,或者增大Java堆内存大小

StackOverFlow栈溢出

方法调用深度过高,简称栈帧太大或者JVM栈太小

unable to create new native thread无法创建线程

每个线程都有独立的方法区和JVM栈区
当线程过多,这两个区的内存会很快被瓜分干净

PermGen Space永久代溢出

JDK1.6之前 String.intern方法会将对象转移到方法区(现今HotSpot虚拟机的永久代),并返回实例引用,如果已经存在则会直接返回引用

1.7之后,intern只会将String类的首次实例放进方法区,之后的不会再放进,这就是所谓的“去永久化”

直接内存溢出

就是主机内存直接不够了

有关GC的三个问题

哪些内存需要回收?

通过算法明确哪些对象需要被回收,存在两种算法:

计数算法(JVM没有采用)

一个对象存在一个地方引用它时,他的计数器+1,即记录引用数量,当计数器为0说明不再被使用,可以回收。但是如果出现循环引用则无法回收,A->B,B->A,虽然都没用了但是都无法回收

可达性分析算法

GC Roots是什么?是指JVM栈中正在调用方法中引用的对象,永久代静态变量和常量引用的对象,本地方法栈中native方法引用的对象这四种对象的集合。

该算法可以通过遍历GC Roots来判断对象是否可达

这里回顾一下四种引用

什么时候回收?

当第一次扫描判断不可达的对象会被标记,并由虚拟机判断该对象是否有必要执行finalize方法,如果finalize方法被覆盖或者已经调用过则会被回收

如果没被调用过,则会被判定为有必要执行,然后该对象会被移动到F-Queue队列中,并由Finalizer线程去执行队列中每个元素的Finalize方法

这时会开启第二次标记,如果对象在finalize方法中成功抱上大腿拯救自己了,则会退出队列,如果没有拯救成功那就会真被回收了

方法区(永久代)会被回收吗?

会,但是回收效率低,需要触发Full GC,把废弃常量和无用类回收掉

什么是无用类?没有任何实例对象、对应的ClassLoader已经被回收、对应的Class类没有任何引用,且没有地方会通过反射访问它

如何回收?

四种回收算法

标记清除:只删除

标记整理:删除之后重新排列整理

复制:平分出2个区,删除当前区域之后两个区直接互换

分代回收:Eden新生代,2个Supervisor,Old老年代,PermGem永久代

集中收集器

Serial:复制算法,单线程,阻塞运行

ParNew:复制算法,Serial的多线程版本

Parallel Scavenge:复制算法,多线程,吞吐量大

Serial Old:标记整理算法,单线程,专用于回收Old

Parallel Old:标记整理算法,多线程,吞吐量大

CMS:标记清除,多线程并发收集,最低停顿

G1:分代收集,标记整理,低停顿,预测停顿时间

对象分配

大部分是朝生夕死,优先在Eden分配,不够时发起Minor GC

什么是Minor GC?

Eden+某1个Supervisor的GC

另一个用来存放对象,GC Eden和S0时,把剩余对象都放到S1

下一GC Eden和S1,然后剩余的放到S0

开始前要检查老年代最大连续空间是否足够,不够直接Full GC

新生区(Eden+2个Supervisor)采用复制算法尝试将GC后的存活对象放入另一个Supervisor(可能放不进去,部分进入Old),再看Eden够不够,不够就再尝试将这个对象直接放到Supervisor(可能放不进去),如果Supervisor再不够就要放到Old

长字符串或者数组这类大对象直接放入老年代,所以避免朝生夕死

Eden中经历一次Minor GC之后,依然存活则会被尝试放入Supervisor,放不进去直接进老年代,放的进就放进去,每经过一次Minor GC则会年龄增长一岁,当达到15岁时进入老年代(这个可以通过设置JVM参数改变)

类的加载

加载过程分为5个步骤
  1. 加载

    通过全限定名(com.a.b.aclazz)来获取此类的二进制字节流

    将这个常量,静态变量,编译后代码的运行区域放入方法区

    在方法区生成对应的class对象(类的信息)

  2. 验证

    保证类符合虚拟机要求的标准

  3. 准备

    静态变量赋初值0,等待初始化阶段赋值,如果是常量则直接赋值

  4. 解析

    将类的二进制数据中的符号引用转换为直接引用

  5. 初始化

    静态变量赋初值

加载器有三种

​ BootStrap ClassLoader启动类加载器,用来加载JDK/lib类库
Extention ClassLoader扩展类加载器,用来加载JDK/lib/ext类库
Application ClassLoader应用类加载器,用来加载classpath下的用户自定义class

还可以加入自己定义的类加载器

双亲委派模型

先让父类加载器加载父类,然后逐步递归

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值