JAVA基础笔记3
堆
Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后,会把什么东西放到堆中:类new过的对象(方法,属性),常量池,保存我们所有引用类型的真实对象。
堆内存中还要细分为三个区域:
- 新生区(Young/new):类诞生和成长的地方,甚至死亡。伊甸园区:所有的对象都是在伊甸园区new出来的,伊甸园区满了之后会启动GC(轻量级垃圾回收),然后把存活下来的对象放入到幸存区中。幸存区(to,form):谁是空的谁是幸存区to,比如从伊甸园区下来的对象放入到to了之后,那么to就会跟form换位置,然后to变成form,form变成to。
- 养老区(old):当新生区都满了之后会启动重GC(重量级垃圾回收),把新生区的都清理一遍,存活下来的对象放入到养老区,或者在新生区经历过15次垃圾回收还存活的也会到养老区,当养老区跟新生区都满了之后,就会报OOM错误(堆内存满了)。如下图堆内存运行原理。
- 元空间(永久区) Perm:这个区域常驻内存的,用来存放JDK自身携带的Class对象。Interface元数据。存储的是java运行时的一些环境或类信息,这个区域不存在垃圾回收!关闭JVM虚拟机就会释放这个区域的内存。
- 一个启动类,加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的反射类。不断的被加载,直到内存满,就会报OOM错误。
- JDK1.6之前:永久代,常量池是在方法区中。
- JDK1.7:永久代,但是慢慢的退化了,去永久代,常量池在堆中。
- JDK1.8之后:无永久代,常量池在元空间,元空间又称非堆。
- 元空间逻辑上是在堆里面,但是物理上是不存在堆里面的。
代码查看堆内存:
public static void main(String[] args){
//返回虚拟机试图使用的最大空间
long max = Runtime.getRuntime().maxMemory();//字节 1024*1024
//返回JVM的初始化总内存
long total = Runtime.geyRuntime().totalMemory();
System.out.println("max"+max+"字节"+(max/(double)1024/1024)+"MB");
System.out.println("total"+max+"字节"+(total/(double)1024/1024)+"MB");
}
//默认情况下:分配的总内存是电脑的1/4,而初始化的内存:1/64
//修改堆内存的参数:-Xms1024m -Xmx1024m -xx:+PrintGCDetails
新生区内存+老年代内存总和就是JVM初始化内存的大小。
OOM错误的解决方式:
- 尝试扩大堆内存看结果。
- 分析内存,看一下哪个地方出现了问题(专业工具)。
内存快照分析工具: - MAT,JproFiler(idea里面需要先下载插件)
- 分析Dunmp内存文件,快速定位内存泄漏。
- 获得堆中数据。
- 获得大的对象等等。
-Xms:设置初始化内存分配大小 默认1/64
-Xmx:设置最大分配内存 默认1/4
-XX:+PrintGCDetails:打印GC垃圾回收信息。
-XX:+HeapDunmpOnOutOfMemoryError:生成内存文件,快速定位错误。
GC垃圾回收主要是在伊甸园区和养老区。
当堆内存满了之后就会报一个错误:OutOfMemoerErroe:Java heap space。
GC(垃圾回收)
JVM在进行GC时,并不是对这三个区域统一回收。大部分的时候,回收都是新生代。
- 新生代
- 幸存区(form,to)
- 老年区
GC两种类:轻GC(普通的GC),重GC(全局GC)。
题目:
- JVM的内存模型和分区,详细到每个区放什么?
- 堆里面的分区有哪些?Eden,form,to,老年区,说说他们的特点。
- GC的算法有哪些?标记清除法,标记压缩,复制算法,引用计术器。怎么用的?
引用计数法
复制算法