方法区:又叫静态区,被所有的线程共享;方法区包含所有的class和static变量;方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
栈:Java中一个线程一个栈区,每一个栈中的元素都是私有的,不被其他栈所访问。栈有后进先出的特点,栈中的数据大小与生存期都是确定的,缺乏灵活性,但是,存取速度比堆要快,仅次于CPU中的寄存器。java中的8中基本数据类型,局部变量都是存在栈中,也可以理解为这些数据大小基本都是固定的。
堆:Java中只有一个堆,被所有线程共享。简单说就是new出来的对象和数组都存在堆中,大小是可以动态变化的,也正是因为这个原因导致堆中数据访问速度慢。垃圾回收机制检测到某对象未被引用时,则自动销毁该对象。java中的包装数据类型包括Byte、Short、Integer、Long、Float、Double、Boolean、Character这些数据类型都是存放在堆中的
JVM的内存结构(JDK8之前):Young Generation(Eden、From、To)、OldGeneration、Permanent Generation
持久带,主要是存放方法区。JDK8以后,Oralce-Sun Hotspot中移除了持久代,用Metaspace代替,元空间的本质和永久代类似,都是对JVM规范中方法区的实现两者的区别是Metaspace是在系统本地内存,它不在于JVM内;
也就是这时候的JVM堆空间大小 = Young Generation + OldGeneration
Metaspace代替永久带的原因:永久带的初始化内存-XX:MaxPermSize可能不够用,又不易调整;因为不在JVM内,避免了GC时候的扫描;
新生带,当空间不足时触发Minor GC ,使用复制清除算法(Copinng算法),原因是年轻代每次GC都要回收大部分对象。新生代里面分成一份较大的Eden空间和两份较小的Survivor空间。每次只使用Eden和其中一块Survivor空间,然后垃圾回收的时候,把存活对象放到未使用的Survivor(划分出from、to)空间中,清空Eden和刚才使用过的Survivor空间。在GC过程中,新对象创建都存再新生带中的Eden区,Eden区不够大时会复制对象到剩下的两个S区(二选一),以此类推当S区满时对象会被复制到OldGeneration。这种流程回收避免的全部区域回收,提高了效率。
老年代,老年代空间不足时触发Major GC,采用标记-整理算法(mark-compact),原因是老年代每次GC只会回收少部分对象。
Full GC:full gc由System.gc()触发,只是建议,不一定执行,会清理年轻代和老年代以及持久带。
可达性分析法 :
1.通过一系列“GC Roots”对象作为起点进行搜索,如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的。不可达对象不一定会成为可回收对象。进入DEAD状态的线程还可以恢复,GC不会回收它的内存。(把一些对象当做root对象,JVM认为root对象是不可回收的,并且root对象引用的对象也是不可回收的)
2、 以下对象会被认为是root对象:(1) 虚拟机栈(栈帧中本地变量表)中引用的对象 (2) 方法区中静态属性引用的对象 (3) 方法区中常量引用的对象 (4) 本地方法栈中Native方法引用的对象
3、 对象被判定可被回收,需要经历两个阶段:(1) 第一个阶段是可达性分析,分析该对象是否可达 (2) 第二个阶段是当对象没有重写finalize()方法或者finalize()方法已经被调用过,虚拟机认为该对象不可以被救活,因此回收该对象。(finalize()方法在垃圾回收中的作用是,给该对象一次救活的机会)
4、 方法区中的垃圾回收:(1) 常量池中一些常量、符号引用没有被引用,则会被清理出常量池 (2) 无用的类:被判定为无用的类,会被清理出方法区。判定方法如下:A、 该类的所有实例被回收 B、 加载该类的ClassLoader被回收 C、 该类的Class对象没有被引用 5、 finalize(): (1) GC垃圾回收要回收一个对象的时候,调用该对象的finalize()方法。然后在下一次垃圾回收的时候,才去回收这个对象的内存。(2) 可以在该方法里面,指定一些对象在释放前必须执行的操作。
1.常用到的JVM参数:-Xms –Xmx –Xss
-Xms 设置堆得初始化大小,–Xmx设置堆最大大小,两个参数都是对堆的性能调优参数,一般两个值设置一样,如果不一样,当Heap不够用,会发生内存抖动。一般都调大这两个参数,并且两个大小一样。-Xss是设置每一个线程栈的大小,影响堆栈调用的深度,当堆内存一定的情况下,可以设置较小的线程栈的值来增加可以创建的线程数量。
2.一般情况下,年轻对象放在eden区,当第一次GC后,如果对象还存活,放到survivor区,此后,每GC一次,年龄增加1,当对象的年龄达到阈值,就被放到tenured老年区。这个阈值可以同构-XX:MaxTenuringThreshold
设置。如果想让对象留在年轻代,可以设置比较大的阈值。
3.一个不稳定的堆并非毫无用处。在系统不需要使用大内存的时候,压缩堆空间,使得GC每次应对一个较小的堆空间,加快单次GC次数。基于这种考虑,JVM提供两个参数,用于压缩和扩展堆空间。(1)-XX:MinHeapFreeRatio
参数用于设置堆空间的最小空闲比率。默认值是40,当堆空间的空闲内存比率小于40,JVM便会扩展堆空间 (2)-XX:MaxHeapFreeRatio
参数用于设置堆空间的最大空闲比率。默认值是70, 当堆空间的空闲内存比率大于70,JVM便会压缩堆空间。(3)当-Xmx和-Xmx相等时,上面两个参数无效
4.尝试使用大的内存分页:使用大的内存分页增加CPU的内存寻址能力,从而系统的性能。-XX:+LargePageSizeInBytes
设置内存页的大小
5.使用非占用的垃圾收集器。-XX:+UseConcMarkSweepGC
老年代使用CMS收集器降低停顿。
(1)jps(Java Process Status):输出JVM中运行的进程状态信息(现在一般使用jconsole) (2)jstack:查看java进程内线程的堆栈信息。(3)jmap:用于生成堆转存快照 (4)jhat:用于分析jmap生成的堆转存快照(一般不推荐使用,而是使用Ecplise Memory Analyzer) (3)jstat是JVM统计监测工具。可以用来显示垃圾回收信息、类加载信息、新生代统计信息等。(4)VisualVM:故障处理工具
(full GC指的是清理整个堆空间,包括年轻代和永久代) (1) 首先用命令查看触发GC的原因是什么 jstat –gccause 进程id (2) 如果是System.gc(),则看下代码哪里调用了这个方法 (3) 如果是heap inspection(内存检查),可能是哪里执行jmap –histo[:live]命令 (4) 如果是GC locker,可能是程序依赖的JNI库的原因