定义
像程序计数器、本地方法栈、虚拟机栈都是线程私有的,而堆(Heap)、方法区都是线程共享的区域。通过new关键字创建的对象都会使用堆内存。
特点
- 它是线程共享的,堆中对象都需要考虑线程安全的问题。
- 有垃圾回收机制,即堆中不再被引用的对象就会当成垃圾进行回收,已释放空闲的内存,这样不至于堆被创建的对象给撑爆。
堆内存溢出
堆内存有垃圾回收器,为何还会存在堆内存溢出问题呢?
比如,对象被当做垃圾回收的条件是这个对象没人再使用他,但是如果不断的产生对象,而产生的这些新对象仍然有人在使用他呢?是不是意味着这些对象不能作为垃圾呢,这样达到一定数量之后,堆内存可能就会被耗尽,所以会堆内存溢出。
代码演示
// 演示堆内存溢出
public class Demo {
public static void main(String[] args) {
int i = 0;
try {
List<String> list = new ArrayList<>();
String a = "asd";
while (true) {
list.add(a);
a = a + a;
i++;
}
}catch(Throwable e) {
e.printStackTrace();
print(i);
}
}
}
有list集合,还有初始值为“asd”的字符串,然后每循环一次把字符串对象加入到list集合,并且每次循环时让字符串拼接,生成新的字符串。运行之后,过了一点时间后,就会出现异常了。
这里出现的异常信息是“java.lang.OutOfMemoryError: Java heap space”,即Java的堆空间不足导致的内存溢出错误。
此处例子中出现堆内存异常的原因是,这里的List集合是不会被垃圾回收的,因为它一直在作用范围内被用着,而越来越变长的字符串对象也一直被追加到list中,所以随着字符串对象越来越多,堆控件就会被占满了。
网友1:字符串字面量赋值是在方法区中
网友2:这里好像有问题
本人评:此处没必要纠结这些小问题小疑问,重点是大致了解相关概念和相关情况即可。
堆内存诊断
jps工具
查看当前系统中有哪些Java进程,并且把它们的进程编号显示出来。
jmap工具
当用jps工具拿到进程编号之后,可以用jmap工具去查看这个进程在堆内存的占用情况。但是jmap他只能查询某一个时刻的堆内存的占用情况。如果想查看连续的情况,即随着时间的流逝他对堆内存的占用情况的话,就得用下面的。
jconsole工具
图形界面的,多功能的监测工具,可以连续监测。
据说更好的工具jvisualvm
和上面一样,也是图形化界面,监测的好像更多。
简单案例
// 演示堆内存
public class Demo {
public static void main(String[] args) throws InterruptedException {
print("1...");
Thread.sleep(30000);// 打印1后,等待30秒。(主要是留给我一段时间敲那些命令)
byte[] array = new byte[1024 * 1024 * 10];//10mb 30秒之后,分配了一块儿内存,由于他是用new创建的,所以他占用的是堆空间
print("2...");
Thread.sleep(30000);// 打印2后,再等待30秒
array = null;// 然后把引用给null,意味着这个array变量不会再引用刚才的对象了,即array可以被垃圾回收了
System.gc();// 垃圾回收
print("3...");// 回收后输出3(这个时候再检测看看堆内存的变化情况)
Thread.sleep(1000000L);
}
}
通过这个案例,目的是要在不同时间点上查看堆内存的占用情况:运行程序后,在本地java文件所在的目录里打开终端,在30秒的时间内输入些命令。
jps
执行后,此例子下,输出了如下信息:
13696 Main
13364
18756 Demo
10040 RemoteMavenServer
16568 Launcher
18168 Jps
在这里就看到了“18756 Demo”。接着执行如下:
jmap -heap 18756
执行后出现一堆信息(稍后解释)。
这时候,过了30秒,所以程序输出2了,然后再30秒之间,继续写命令查看此时的占用情况,亮点是因为刚刚创建了array对象。
jmap -heap 18756
执行后出现一堆信息(稍后解释)。
过了30秒后,array被null,而且垃圾回收了,输出3之后,再检测一遍。
jmap -heap 18756
以上是在三个时间点上分别监测了一下,也就是在三个时间点,抓去了内存的快照信息,大概结果如下:
在第一次输出的内容中,可以看到“Heap Useage:”部分,这里就是显示堆内存的占用情况。里面有“Eden Space:”区域是指新创建的对象使用的区域(比如这里的array),但此时的信息是创建array对象之前的信息,所以这时候还没有array,然后看到这个区域的used项显示使用的是6MB,而总容量(capacity项)是64MB,这个是第一个时间点的信息。
第二次输出的内容是创建byte数组对象之后的情况,在这次的输出内容中,再找到“Heap Useage:”部分,再看一下他的“Eden Space:”区域,可以看到里面的used项显示内存使用了16MB,比刚才多了10MB,因为新创建的数组大小就是10MB。
第三次输出内容是等垃圾回收之后的内存快照信息,在输出内容中,可以看到“Eden Space:”区域的used项的值是1MB,这说明执行垃圾回收之后,刚才16MB中的不用的垃圾都被回收掉了,包括array这个10MB的对象。
还可以使用图形化工具jconsole或jvisualvm(好像更好)动态时时
查看相关内存占用情况,因为本人感觉没必要特意整理这些,所以就算
了。