java jvm内存地址_Java JVM内存那些事儿
发布日期:2021-06-24 16:33:23 浏览次数:2 分类:技术文章

本文共 5511 字,大约阅读时间需要 18 分钟。

一、Java内存模型

系统的地址空间 可以划分为 内核空间 和用户空间。 内核空间主要是系统运行空间,包含连接系统硬件和调度程序 以及提供联网 和虚拟内存等服务C进程运行空间,而用户空间是Java程序实际运行时 所在内存空间。

JVM架构图如图所示,其中类加载器(Class Loader)负责依据特定格式,加载class文件到内存;执行引擎(Execution Engine)负责 对命令进行解析;本地库接口(Native Interface) 负责融合不同开发语言的原生库为Java所用。

而 Runtime Data Area 为 JVM中java运行内存部分。

dcf870fc3019932bd5ee04ffc8f36bff.png

将Java运行内存 从线程角度 可分为线程私有内存和线程共享内存。

69afc752cf57425b3586e5c6a54386dd.png

程序计数器

1,程序计数器 当前线程所执行的字节码行号指示器(逻辑)

2,CPU改变计数器的值 来选取下一条需要执行的字节码指令

3,和线程是一对一的关系即“线程私有”

4, 对java方法计数, 如果是Native方法则计数器值为Undefined

5,不会发生内存泄漏

虚拟机栈

1,虚拟机栈Java方法执行的内存模型

2,虚拟机栈包含多个栈帧,其中每个方法执行 都会创建一个栈帧 并放入虚拟机栈, 当方法执行完后再将该栈帧移除。

3,一个栈帧包括 局部变量表、操作栈、动态连接、返回地址等

4,虚拟机栈 有大小限制,当超出最大栈深度后,会发生SOF异常

java.lang.StackOverflowError异常

原因 :递归过深,栈帧数 超过虚拟机栈深度

避免方法:限制递归的深度或使用循环代替递归操作

复制代码

5,虚拟机栈为线程私有,运行时结构图如下所示:

java.lang.OutOfMemoryError异常:虚拟机栈过多 引发

复制代码

eb82a0ae01af3731e28d7bd068b79ff0.png

本地方法栈

与虚拟机栈相似,主要作用是标注了Native的方法

方法区

方法区是JVM的一种规范,保存java类的相关信息(包括Method、Filed等)。元空间 和永久代 都是方法区的实现。

永久代(PermGen)

jdk8之前,使用永久代。永久代使用的是JVM内存。

但是从jdk 7开始,原先位于永久代中的字符串常量池被移入到堆内存中。

元空间(MetaSpace)

jdk8开始 使用元空间替代永久代。元空间使用堆内存。

元空间 相对永久代 具有如下优势:

1,字符串常量池 存在永久代中,容易出现性能问题和内存溢出。

2,类和方法的信息大小难以确定,给永久代的大小指定困难

3,永久代 会为GC带来不必要的复杂度

4,方便HotSpot与其他JVM 如Jrockit的集成

Java堆

1,虚拟机启动时创建,被所有线程共享,是对象实例的分配区域

2,GC管理的主要区域

java内存模型中 堆 和栈的联系和区别

联系:

引用对象、数组时,栈里定义变量保存堆中目标的首地址

区别:

1,管理方式:栈自动释放,堆需要GC

2,空间大小:栈比堆小

3,碎片化: 栈产生的碎片 远小于堆

4,分配方式: 栈支持静态和动态分配,而堆只支持动态分配

5,效率:栈的效率 比堆高

面试:intern()方法的作用

不同JDK版本之间 intern()方法的区别--JDK6 VS JDK6+

JDK6:

当调用intern()方法时,如果字符串常量池先前 已创建出该字符串对象,则返回池中的该字符串的引用。否则,将此字符串对象添加到字符串常量池中,并且返回该字符串常量对象的引用。

JDK6+:

当调用intern()方法时,如果字符串常量池先前已创建出该字符串常量,则返回池中的该字符串的引用。否则,如果该字符串对象已经存在Java堆中,则将堆中对此对象的引用添加到字符串常量池中,并且返回该引用;如果堆中不存在,则在池中创建该字符串并返回其引用。

Java内存分配策略

1,静态存储:编译时 确定每个数据目标在运行时的存储空间需求

2,栈式存储:数据区需求 在编译时未知, 运行时模块入口前确定

3,堆式存储:编译时或运行时模块入口都无法确定,只能运行时动态分配

二、Java垃圾回收机制

1,垃圾对象判断的标准

当虚拟机发现该对象没有被其他对象引用时,则将该对象判断为可以回收的垃圾对象。检测对象是否为被其他对象引用可以使用 引用计数法,和可达性分析算法。

引用计数法

1, 通过判断对象的引用数量 来决定对象 是否可以被回收

2, 每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1

3, 任何引用计数为0的对象实例 可以被当作垃圾收集

优点:执行效率高,程序执行受影响较小

缺点:无法检测出 循环引用的情况,导致内存泄漏

可达性分析算法

通过判断对象的引用链是否可达 来决定对象是否可以被回收

可以作为GC root的对象:

1,虚拟机栈中引用的对象(栈帧中的本地变量表)

2,方法区中的常量引用的对象

3,方法区中的类静态属性引用的对象

4,本地方法栈中JNI(Native方法)的引用对象

5,活跃线程的引用对象

2,垃圾回收算法

标记-清除算法(Mark and Sweep)

标记清除算法分为标记和清除两个阶段:

标记阶段: 从根集合开始进行扫描,对存活的对象 进行标记

清除阶段:对堆内存从头到尾进行线性遍历,回收不可达对象内存

缺点

碎片化,造成内存不连续

复制算法(Copying)

步骤

1,将可用内存分为对象面和空闲面

2,对象在对象面上创建

3,存活的对象被从对象面 复制到空闲面

4,将对象面所有对象内存清除

优点

解决碎片化问题

顺序分配内存,简单高效

适用于对象存活率低的场景

缺点

不适合 对象存活率高的场景,会进行大量复制操作

可用空间减半

标记-整理算法(Compacting)

标记阶段: 从根集合进行扫描,对存活的对象进行标记

整理阶段: 移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收

优点

1,避免内存的不连续行

2,不用设置两块内存互换

3,适用于存活率高的场景

分代收集算法(Generation Collector)

分代收集算法是以上垃圾回收算法的组合拳,按照对象生命周期的不同划分区域 以采用不同的垃圾回收算法。能够明显提高提高JVM的回收效率。

分代收集算法的GC 分为 Minor GC 和 Full GC。

Minor GC: 采用复制算法

Full GC: 采用标记清理算法,或标记整理算法, 老年代GC时,会触发对整个堆内存的垃圾回收

分代收集算法将内存区域分为年轻代、老年代(JDK8开始取消永久代,使用元空间代替)并使用不同的GC进行垃圾回收。

年轻代 (Young Generation)

1,将内存区域进一步划分为 Eden区,和2个Survivor区

2,采用Minor GC(复制算法)

3,尽可能快速地收集掉那么些生命周期短的对象

4,年轻代对象 可以晋升到老年代

老年代 (Old Generation)

1,存放生命周期较长的对象

2,使用Full GC(标记清理算法 或者标记整理算法)

年轻代对象如何晋升到老年代

1,经历一顶Minor GC次数 依然存活的对象

2,Survivo区中存放不下的对象

3,新生成的大对象 (-XX:+PretenuerSizeThreshold)

触发Full GC的条件

1,老年代 空间不足

2,永久代空间不足(jkd 8 之前)

3,CMC GC时 出现 promotion failed, concurrent mode failure

4,Minor GC晋升到老年代的平均大小 大于老年代的剩余空间

5,调用System.gc()

6,使用RMI来进行RPC 或管理的JDK应用,每小时执行1次 Full GC

5883ff35c0e314f3eaf5efd2b37dbf80.png

相关面试题

finalize()方法

1,与C++的析构函数不同, 析构函数调用确定, 而finalize()调用不确定

2, JavaGC时 会对对象做两次标记,

第一次标记, 判断对象是否要执行finalize()方法,如果需要执行,就将对象放置于F-Queue队列中,并在稍后由虚拟机创建的一个低优先级的线程触发执行 该对象的finalize()方法。

3,该方法执行 随时可能被终止

4,作用,是给予对象最后一个重生的机会

Java中的引用

强引用(Strong Reference)

最普遍的引用;

抛出OOM异常时终止程序时,也不会回收具有强引用的对象;

通过将对象设置为null 来弱化引用,使其被回收

软引用(Soft Reference)

对象处在有用但非必须的状态;

只有当内存空间不足时, GC会回收该引用对象的内存;

可以用来实现高速缓存

弱引用(Weak Refrence)

非必须的对象, 比软引用更弱一些;

GC时会被回收;

被回收的概率也不大,因为GC线程的优先级比较低;

适用于引用偶尔被使用,且不影响垃圾收集的对象

虚引用(PhantomReference)

不会决定对象的生命周期;

任何时候都可能被垃圾收集器回收;

用来跟踪对象被垃圾收集器回收的活动,起哨兵作用;

必须和引用队列 ReferenceQueue联合使用, 在GC回收后,会将虚引用加入到 与之关联的ReferenceQueue中;

引用队列

1,无实际存储结构,存储逻辑依赖于内部节点之间的关系来表达

2,存储关联的且被GC的软引用、弱引用以及虚引用

引用队列 应用Demo

自定义实例类,以及弱引用类(方便记录和打印名字)

//被创建实例对象

public static class Dog {

public String name;

public Dog(String name) {

this.name = name;

}

@Override

protected void finalize() throws Throwable {

super.finalize();

System.out.println(">>>finalized " + name);

}

}

复制代码//弱引用对象

public class MWeakRef extends WeakReference {

public String name;

public MWeakRef(Dog referent, ReferenceQueue super Dog> q) {

super(referent, q);

name = referent.name;

}

}

复制代码public class ReferenceDemo {

private static ReferenceQueue sRq = new ReferenceQueue<>();

public static void main(String[] args) throws InterruptedException{

ArrayList objects = new ArrayList<>();

for (int i = 0; i < 3; i++) {

objects.add(new MWeakRef(new Dog("dog " + i), sRq));

}

System.out.println("----------- first check -----------");

checkReferenceQueue();

System.out.println("----------- start gc -----------");

System.gc();

Thread.sleep(1000);

System.out.println("----------- second check -----------");

checkReferenceQueue();

}

private static void checkReferenceQueue() {

Reference extends Dog> ref;

while ((ref = sRq.poll()) != null) {

Dog dog = ref.get();

System.out.println("finalized wrf:" + ((MWeakRef) ref).name + " , ref to:" + dog);

}

}

}

复制代码

程序运行结果如下所示,第一次 检查引用队列时,弱引用对象并没有被回收,所以打印队列为空;然后调用System.gc(), 请求gc时,对象依次被回收,再次检查引用队列时,发现刚刚被回收的引用已被添加到引用队列中

----------- first check -----------

----------- start gc -----------

>>>finalized dog 2

>>>finalized dog 1

>>>finalized dog 0

----------- second check -----------

finalized wrf:dog 0 , ref to:null

finalized wrf:dog 2 , ref to:null

finalized wrf:dog 1 , ref to:null

Process finished with exit code 0

复制代码

完~

(如果纰漏,欢迎指出)

转载地址:https://blog.csdn.net/weixin_33865450/article/details/114140709 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:getwayworker timer_Laravel + Workerman 实现多进程定时器任务
下一篇:java多线程电子时钟代码实例_java多线程电子时钟代码实例

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年04月14日 15时35分50秒