一:java内存分配策略
站在线程的角度来看分为:
- 线程私有:栈,本地方法栈,程序计算器
- 线程共有:堆,方法区
栈:
是以栈帧的方式来存储方法调用的过程,并存储方法调用过程中的基本变量,以及对象引用。
栈上分配:
对于线程私有的对象,基于逃逸分析,会将他分配到栈中,逃逸分析:判断对象的作用域是否逃出方法体。server模式才能进行逃逸分析
-Xmx10m和-Xms10m:堆的大小
-XX:+DoEscapeAnalysis:启用逃逸分析(默认打开)
-XX:+PrintGC:打印GC日志
-XX:+EliminateAllocations:标量替换(默认打开)
-XX:-UseTLAB 关闭本地线程分配缓冲
栈上分配的效果
同样的User的对象实例,分配100000000次,启用栈上分配,只需6ms,不启用,需要3S。
堆:是存储java中的对象
类加载器:
分为启动类加载器和扩展类加载器负责加载JDK的类,应用类加载器负责加载用户类,还有自定义的类加载器
对象的创建过程:
- 执行相应的类加载过程,
- 分配内存:分配内存有2中方式,指针碰撞和空闲列表,
- 指针碰撞时针对内存是规整有序,在分配内存时,只需要将指正向空闲方移动
- 空闲列表是针对内存不规整有序,在分配内存是,从列表中找到相应的内存空间,并更新列表记录
- 初创化字段数据,设置默认值,如int值为0,boolean值为false等等
- 设置,将对象的一些信息,哈希码,代龄等一些数据放入对象头中
- 执行构造方法
对象在内存中由:对象头,实例数据,对齐填充组成
对象的访问:
对象的访问方式有2中,句柄和指针
- 句柄:需要维护一个句柄池用来存储对象的句柄地址,句柄中包含了对象实例数据和具体的地址信息
- 指针:直接通过对象地址访问
JDK使用指针访问方式进行对象的访问
二:OOM
一般分为2中类型,栈溢出和堆溢出
堆溢出:出现outOfMemoryError 分为2中情况
- 一般是在不停的分配对象,把堆撑爆,GC 超过限制次数
- 分配了巨型对象,java堆空间不足
栈溢出:出现stackOverFlowError 一般是出现了无效递归
三:垃圾回收器
对象的引用:
- 强引用:使用new关键字
- 软引用:使用soft Reference,表示有用但不是必须,可以在OOM前进行回收
- 弱引用:使用weakReference,表示有用但不是必须,可以在下次GC时被回收
- 虚引用:最弱的引用,GC时被回收会有一个通知
垃圾识别:
分辨垃圾通过,引用计数和可达性分析:GC Roots 通过引用链查找对象,当一个对象没有和任何GC Root相连接时,则此对象不可用
垃圾回收算:
- 标记清除:会产生内存碎片
- 复制:将内存划分为2块,只使用其中一块,当被使用的内存用完时,将还存活的对象复制到另外一块内存中,然后清除此块内存中的数据,会缩小原内存空间
- 标记整理:将垃圾清除后多了一个整理的动作,将存活的对象想一段移动
- 分代收集:这种算法时根据对象存活的周期,将对象划分为新生代和老年代,
- 新生代一般是朝生夕死安,按照8:1将内存分为eden区和幸存区,当回收事,将eden和幸存区存活的对象一次性复制到里另一块幸存区,一般使用复制算法
- 老年代:在幸存区中存活了一点的次数默认15次,后将这类对象移动到老年区,老年区对象存活率高,一般使用标记清除,或标记整理
JVM中的垃圾回收器
一般分为 串行,并行,并发三种类型,常用的有:
- Serial/Serial Old 串行,垃圾回收时造成业务线程停顿
- parNew/Serial Old:基本与serial一样,只不过是并行的多线程收集器,新生代使用ParNew,老年代使用Serial Old
- ParallerGC/Parallel Old:是专注吞吐量的垃圾回收器,可以设置垃圾回收停顿的时间和垃圾收集时间占总时间的比率调小停顿时间会造成 新生代空间变小,GC次数变多,最大的区别是可以在参数给虚拟机设立一个优化目标,启动GC自适应的调节策略
- serial或ParNew/CMS:适用于老年代的并发收集器,是以最短回收停顿时间为目标的回收器,基于标记清除算法,将收集步骤分为:
- 初始标记-造成短暂停顿
- 并发标记-不停顿,与业务线程一同进行
- 重新标记--造成短暂停顿
- 并发清除
- G1:适用于新生代和老年代的并行与并发收集器,G1的内存划分与其他收集器不同,它是将堆内存划分为多个大小相同的区域,通过标记来确实是新生代还是老年代,
- 收集步骤分为:
- 新生代GC:将eden区情况,只存在一个幸存区,
- 并发标记:
- 初始标记:短暂停顿,标记GC ROOTs能关联的对象
- 根区域扫描:检查幸存区那些对象可以晋升为老年代
- 并发标记:扫表怎个堆的存活对象
- 重新标记:短暂停顿,堆并发阶段结果进行修正
- 独占清理:产生停顿,并对GC 比例进行排序
- 并发清除:并发进行识别并清理完全空闲的区域,
- 混合收集:对含有垃圾比例高的区域进行回收
当出现内存不足的的情况,也可能进行的FullGC回收
G1中重要的参数:
-XX:MaxGCPauseMillis 指定目标的最大停顿时间,G1尝试调整新生代和老年代的比例,堆大小,晋升年龄来达到这个目标时间。
-XX:ParallerGCThreads:设置GC的工作线程数量
频繁GC问题或内存溢出问题
一、使用jps
查看线程ID
二、使用jstat -gc 3331 250 20
查看gc情况,一般比较关注PERM区的情况,查看GC的增长情况。
三、使用jstat -gccause
:额外输出上次GC原因
四、使用jmap -dump:format=b,file=heapDump 3331
生成堆转储文件
五、使用jhat或者可视化工具(Eclipse Memory Analyzer 、IBM HeapAnalyzer)分析堆情况。
六、结合代码解决内存溢出或泄露问题。
死锁问题
一、使用jps
查看线程ID
二、使用jstack 3331
:查看线程情况