java的内存区域:
1、程序计数器:可以认为是当前线程所执行的字节码的字号指示器,同时在多线程情况下,用来记录当前线程执行的位置,从而当线程切换的时候能找到线程之前执行的位置
2、Java虚拟机栈:主要有局部变量表,存放了编译器可知的数据类型,对象引用,可能出现两种异常
StackOverFlowError 如果虚拟机栈的大小不允许动态扩展,当线程请求栈的深度超过当前虚拟机栈的最大深度的时候
OutOfMemoryError 如果允许动态扩展,并且当线程请求栈时内存用完了,抛出此异常
3、本地方法栈,和虚拟机栈功能类似,区别是虚拟机栈为执行java方法(字节码)服务,本地方法栈则为虚拟机用到的native方法服务
4、堆:所有线程共享,用来存放对象实例,是垃圾收集器管理的主要区域,因此也被称为GC堆,可以被细分为新生代老年代
细分一点包括Eden空间,from Survivor To Survivor空间等
5、方法区:用于存储已被虚拟机加载的类信息,常量,静态变量
6、运行时常量池,是方法区的一部分,用于存放编译期生成的各种字面量和符号引用
hotspot虚拟机:对象的创建:
1、类加载检查:
2、分配内存:两种方式 1指针碰撞 2空闲列表
并发问题解决办法:1cas+失败重试 cas是乐观锁的一种实现方式 2 TLAB本地线程分配缓冲
3、初始化零值
4、设置对象头
5、执行init方法
String s1 = new String("abc");这句话创建了几个对象? 两个,常量池的对象,堆内的对象
首先“abc”字符串在编译期放入常量池,然后new的时候new一份字符串“abc”放入堆,然后java栈的str1指向java堆上的“abc”
Integer 缓存
/**
*此方法将始终缓存-128到127(包括端点)范围内的值,并可以缓存此范围之外的其他值。
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
GC调优策略
GC调优原则:先将机器的jvm参数设置到最优,然后需要根据GC情况分析代码优化:减少创建对象的数量,减少使用全局变量和大对象
GC调优目的:将转移到老年代的对象降低到最小,减少GC的时间
GC调优策略:1、将新生对象留在新生代,因为Full GC成本远高于Minor GC,可以通过 -Xmn调节新生代大小
2、尽量让大对象进入老年代,大对象在新生代可能导致空间不足
3、合理设置进入老年代对象的年龄,减少老年代的内存占用,降低full gc发生的频率
4、设置稳定的堆大小,-Xms初始化堆大小,-Xmx最大堆大小
MinorGC和FullGC
从年轻代空间(包括Eden和Survivor区域)回收内存被称为MinorGC,MajorGC是清理永久代,FullGC是清理整个空间,包括年轻代和永久代
FullGC需要STW,即停止所有活动(stop the world collection),所有的线程都被挂起(除了垃圾收集器)
垃圾回收算法:
1、引用计数,引用计数变为0时,则该对象可回收,不过无法解决两个对象互相引用的问题,因此已不再使用
2、标记清理:从根集(GC Roots)出发寻找所有的引用,找到一个就对其做标记,当追踪完成后,所有未标记的就是需要回收的垃圾,他的标记过程就是可达性分析算法中判断垃圾对象的标记过程
缺点:标记和清除的效率不高,而且标记清除后会产生大量不连续的内存碎片
3、复制算法(copy):将可用内存按容量分为大小相同的两块,每次只用其中一块,当这一块用完了,就将还活着的对象复制到另一块内存上面,然后再把这一块内存空间一次清理掉
比较适合新生代,因为新生代每次收集都会有大量对象死去,只有少量存活,但是一比一太浪费,因此将内存分为较大的Eden(伊甸园)空间和两块较小的Survivor(幸存者)空间,每次使用Eden和一块Survivor,当回收时,将其中还活着的对象一次性复制到另外一块Survivor上,然后清理掉Eden和Survivor,HotSpot默认比例是Eden:Survivor=8:1,也就是只有10%的空间会浪费
4、标记整理:比较适合老年代,因为老年代存活率比较高,该算法标记和上面标记清理算法一样,但是处理的时候不是直接清理,而是将所有的对象向一端移动,然后直接清理掉端边界以外的内存
5、分代收集:当前商业虚拟机采用的算法,根据对象的存活周期将不同的内存划分成几块,一般是把java堆分为新生代和老年代,他们具体使用的回收算法如下:
GC算法 | 优点 | 缺点 | 存活对象移动 | 内存碎片 | 适用场景 |
---|---|---|---|---|---|
引用计数 | 实现简单 | 不能处理循环引用 | |||
标记清除 | 不需要额外空间 | 两次扫描,耗时严重 | N | Y | 老年代 |
复制 | 没有标记和清除 | 需要额外空间 | Y | N | 新生代 |
标记整理 | 没有内存碎片 | 需要移动对象的成本 | Y | N | 老年代 |
常用的垃圾收集器
1、从线程运行情况可以分成三种:1、串行回收,Serial回收器,单线程回收,全程stw。
特点:cpu利用率最高,但是停顿时间比较长
2、并行回收 以Parallel开头的回收器,多线程回收,全程stw
特点:回收时间短,效率高,对吞吐量要求高,一般应用于大型应用,科学计算
3、并发回收,cms与G1,多线程分阶段回收,只有某阶段会stw
cms(Concurrent Mark Sweep)采用了标记清除算法(针对老年代,新生代还是复制算法),不过要复杂一些,分为四个步骤:
初始标记(CMS initial mark)
并发标记(CMS concurrent mark)
重新标记(CMS remark)
并发清除(CMS concurrent sweep)
特点:并发收集,低停顿,响应时间快