JVM 这些知识你知道吗?

165 阅读5分钟

这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战

好多读者朋友,都问我关于JVM相关的知识,这边我上网找了一些资料。
文末有该篇文章的PDF下载方法

一、JVM 内存区域相关

1.JVM 的内存模型以及分区情况和作用

如下图所示:

黄色部分为线程共有,蓝色部分为线程私有。

方法区
用于存储虚拟机加载的类信息,常量,静态变量等数据。


存放对象实例,所有的对象和数组都要在堆上分配。是JVM所管理的内存中最大的一块区域。


Java方法执行的内存模型:存储局部变量表,操作数栈,动态链接,方法出口等信息。生命周期与线程相同。

本地方法栈
作用与虚拟机栈类似,不同点本地方法为native方法执行服务,虚拟机栈为虚拟机执行的Java方法服务。

程序计数器
当前线程所执行的行号指示器,是JVM内存区域最小的一块区域。执行字节码工作时就是利用程序计数器来选取下一条需要执行的字节码指令。

2.Java内存分配

寄存器: 我们无法控制
静态域: static定义的静态成员。
常量池: 编译时被确定并保存在 .class文件中的(final)常量值和一些文本修饰的符号引用(类和接口的全限定名,字段的名称和描述符,方法和名称和描述符)。
非RAM存储: 硬盘等永久存储空间。
堆内存: new创建的对象和数组,由Java虚拟机自动垃圾回收器管理,存取速度慢。
栈内存: 基本类型的变量和对象的引用变量(堆内存空间的访问地址),速度快,可以共享,但是大小与生存期必须确定,缺乏灵活性。

Java堆的结构是什么样子的?什么是堆中的永久代(Perm Gen space)?

JVM的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在JVM启动的时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收。

堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉之前,他们会一直占据堆内存空间。

3.Java中会存在内存泄漏?简述一下

所谓内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。

Java中有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象变成了孤儿的时候,对象将自动被垃圾回收器从内存中清除掉。由于Java使用有向图的方式进行垃圾回收管理,可以消除引用循环的问题,例如有两个对象,相互引用,只要它们和根进程不可达的,那么GC也是可以回收它们的。

Java中的内存泄漏的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是Java中内存泄漏的发生场景,通俗的说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,但是这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是java中可能出现内存泄漏的情况,例如: 缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。

检查Java中的内存泄漏,一定要让程序将各种分支情况都完整执行到程序结束,然后看某个对象是否被使用过,如果没有,则才能判定这个对象属于内存泄漏。

如果一个对部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持久外部类的实例对象,这个外部类对象不会被垃圾回收,这也会造成内存泄漏。

内存泄漏的另外一种情况: 当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的哪些参与计算哈希值的字段了,否者,对象修改后的哈希值与最初存储进HashSet集合中时哈希值就不同了,在这种情况个,即使在contains方法使用对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄漏。