Java堆的唯一目的就是创建实例对象,几乎所有的对象都在堆上分配内存,如果堆空间分配内存出现问题可见是非常严重的。当Java 堆内存出现问题时,我们应该怎么去优化呢?在学习堆内存优化之前,我们要先了解一下有关堆内存优化的参数。堆在实现时,既可以实现成固定大小的,也可以是可扩展的,当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。
在这里通过程序测试一下:
public class HeapTest {
public static void main(String[] args) {
long maxMemory = Runtime.getRuntime().maxMemory() ; //返回 Java 虚拟机试图使用的最大内存量。
long totalMemory = Runtime.getRuntime().totalMemory() ; //返回 Java 虚拟机中的内存总量。
System.out.println("最大分配内存 = " + maxMemory + "(字节)、"
+ (maxMemory / (double)1024 / 1024) + " MB");
System.out.println("初始化分配内存大小 = " + totalMemory + "(字节)、"
+ (totalMemory / (double)1024 / 1024) + " MB");
}
}
输出:
最大分配内存 = 1883242496(字节)、1796.0 MB
初始化分配内存大小 = 128974848(字节)、123.0 MB
博主的电脑是8G 的内存大小,可见在默认的情况下默认的:堆分配的内存是总内存的“1 / 4”、而初始化的内存为“1 / 64”。接下来我们也看一下运行程序时输出的GC 信息日志,我们右键Run As->Run Configuratios会出现这样的一个对话框,我们在红线区域输入我们要设置的堆最大分配内存与初始化内存大小,这里都设置了1G 大小的内存。
我们点击Run,下面是输出的结果:从下面的图可以看出,Heap 下有三个内存空间PSYoungGen (新生代)、ParOldGen(老年代)、Metaspace(元空间),由于我们设置了堆的初始化大小,从输出结果可以看出为 981.5 MB大小。 但是经过计算PSYoungGen 与 ParOldGen 就占据了所有了初始化堆内存空间,那Metaspace 在哪里呢?其实在Java 8 之前是不存在 Metaspace 的,在Java 8 中永久代被移除了方法区,为了替代永久代于是Metaspace 诞生了,但是它和之前的永久代的保存的数据并不完全一样。Java 运行时数据区域介绍传送~ 博主使用的是jdk1.8,如果是jdk1.7以前的版本那么就不会看到Metaspace 而是PSPermGen(永久代),当然这也可以证明在jdk1.8 中永久代已经被移除了。方法区是堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),也就是说它并不是堆内存空间。这也难怪为什么PSYoungGen 和 ParOldGen 的内存总和就占据了所有的堆内存初始化空间。
下面我们就来看一下堆内存溢出的情况:这里我创建了一个集合并不断的创建新对象添加到集合中去。
import java.util.ArrayList;
import java.util.List;
public class TestOutOfMemoryError {
public static void main(String[] args) {
List<Test> list = new ArrayList<>();
while(true){
list.add(new Test());
}
}
}
class Test{}
然后不一会就出现了这样的情况:报了一个OutOfMemoryError 的异常,这个异常告诉我们堆内存已经溢出了,你写的程序有问题(如果有的同学也写了这样一段代码,结果很久也没报异常,不是不会报异常,只是堆内存还没有溢出,你只需要慢慢等就好啦,其实博主在运行之前就偷偷将参数都设置成了1m 大小…….)。
但是我们只知道是内存溢出了,但是哪里溢出了?我们不能够通过该异常分析出异常出现的地方,这时我们可以使用 -XX:+HeapDumpOnOutOfMemoryError 参数导出一个.hprof 文件然后分析该文件快速定位到内存泄漏的地方。导出该文件需要安装一个插件,博主使用的eclipse ,可以从eclipse 的官网上复制一个链接 http://download.eclipse.org/mat/1.7/update-site/,按照下面的操作在eclipse 安装该插件即可。
下面我们就来看一下该文件都是有些什么?运行以后刷新我们的工程。
刷新工程以后就会发现我们的项目下多了一个类似于java_pid6504.hprof 的文件。在博主的eclipse 下打开是一堆乱码,于是需要下载一个解析该文件的软件MemoryAnalyzer,直接在百度上搜索即可。
用MemoryAnalyzer 打开该文件,发现打开该文件后是这个样子的,然后就可以愉快的知道堆创建对象的过程了。
现在博主还不能完全解读该文件,等什么时候可以熟练解读该文件的时候博主再更新一篇介绍该文件的博文吧。