基本概念:
1.栈是运行时的单位,而堆是存储的单位。堆中存的是对象,栈中存的是基本数据类型和堆中对象的引用。
问题1:为什么不把基本类型放堆中呢?基本类型1~8个字节和对象引用4字节所需空间小,不会出现动态增长,因此在程序运行时,他们的处理方式是统一的。
问题2:Java中的参数传递时传值呢还是传引用?程序运行永远都是在栈中进行的,因而参数传递时,只存在传递基本类型和对象引用的问题,不会直接传对象本身。
2.Java对象的大小:
基本数据类型:boolean 1-4字节,byte 1字节,chart 2字节,short 2字节,int 4个字节,float 4个字节,long 8个字节,double 8个字节。
空Object对象:8byte,Object ob = new Object();大小是4byte栈引用+8byte堆大小,Java对象大小都大于8byte。
基本类型包装类:至少是12byte(声明一个空Object至少需要的空间),而且12byte没有包含任何有效信息,因为Java对象大小是8的整数倍,因此一个基本类型包装类的大小至少是16byte。
3.引用类型:
强引用:声明对象时虚拟机生成的引用,垃圾回收时如果被强引用,则不会被垃圾回收。
软引用:一般被做为缓存来使用,垃圾回收时会根据当前系统的剩余内存来决定是否对软引用进行回收。
弱引用:都是作为缓存来使用,一个垃圾回收周期内是一定会被回收掉的。
虚引用:任何时候都可能被垃圾回收器回收。
基本垃圾回收算法:
1.引用计数 :对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。
2.标记-清除:分两阶个段,第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时会产生内存碎片。
3.复制:内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中,复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。
4.标记-整理:此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
分代垃圾回收:
不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。
年轻代(Young Generation):所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
年老代(Old Generation):在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
持久代(Permanent Generation):用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=<N>进行设置。
垃圾回收的触发:
1.Scavenge GC:一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC。
2.Full GC:对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:年老代(Tenured)被写满,持久代(Perm)被写满 ,System.gc()被显示调用 ,上一次GC之后Heap的各域分配策略动态变化。
典型配置:根据内存大小/响应时间需求/吐露量需求配置,参考:https://blog.csdn.net/iteye_11086/article/details/81796200 https://blog.csdn.net/iteye_11086/article/details/81798490
JVM调试工具:1.免费-JDK自带Jconsole和VisualVM ,2.付费-JProfiler
参考博客:https://blog.csdn.net/iteye_11086/article/details/81792553