点击上方 "程序员小乐"关注公众号, 星标或置顶一起成长
每天早上8点20分, 第一时间与你相约
每日英文
Don’t blame people for disappointing you. Blame yourself for expecting too much.
不要埋怨别人让你失望了。怪你自己期望太多。
每日掏心话
人生之所以精彩,是他愿意全然的接受一切。生命之所以可贵,是他愿意尊重一切的生命。
来自:秃桔子 | 责编:乐乐
链接:cnblogs.com/godoforange/p/11565505.html
程序员小乐(ID:study_tech)第 640 次推文 图片来自网络
往日回顾:爬虫平台的架构实现和框架的选型
01 三大原则+担保机制
JVM分配内存机制有三大原则和担保机制
优先分配到eden区
大对象,直接进入到老年代
长期存活的对象分配到老年代
空间分配担保
02 对象优先在Eden上分配
如何验证对象优先在Eden上分配呢,我们进行如下实验。
打印内存分配信息
首先代码如下所示:
public class A {
public static void main(String[] args) {
byte[] b1 = new byte[4*1024*1024];
}
}
代码很简单,就是创建一个Byte数组,大小为4mb。
-verbose:gc -XX:+PrintGCDetails
在我们运行后,结果如下所示。
Heap
手动指定收集器
我们可以看在新生代采用的是Parallel Scavenge收集器
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC
运行结果如下:
Heap
其实JDK默认的不是Parallel收集器,但是JDK会依照各种环境来调整采用的垃圾收集器。
查看环境的代码如下:
java -version
而Serial收集器主要用在客户端的。
eden分配的验证
我们看到现在eden区域为34432K,使用了19%,那我们来扩大10倍是否eden就放不下了呢?
public class A {
public static void main(String[] args) {
byte[] b1 = new byte[40*1024*1024];
}
}
运行结果如下:
Heap
显然,我们还是正常运行了,但是eden区域没有增加,老年代区域却增加了,符合大对象直接分配到老年代的特征。。
所以我们适当的缩小每次分配的大小。
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
这里我们限制内存大小为20M
然后我们运行我们的代码:
代码如下所示:
public class A {
public static void main(String[] args) {
byte[] b1 = new byte[2*1024*1024];
byte[] b2 = new byte[2*1024*1024];
byte[] b3 = new byte[2*1024*1024];
byte[] b4 = new byte[4*1024*1024];
System.gc();
}
}
运行结果如下:
[GC (Allocation Failure) [DefNew: 7129K->520K(9216K), 0.0053010 secs] 7129K->6664K(19456K), 0.0053739 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
我们可以发现在eden区域为8192K 约为8M
而原先的b1,b2,b3为6M,被分配到了tenured generation。
原先的Eden区域如下所示,在分配完,b1,b2,b3后如下所示。
而查看日志的时候,我们发生了俩次GC。
[GC (Allocation Failure) [DefNew: 7129K->520K(9216K), 0.0053010 secs] 7129K->6664K(19456K), 0.0053739 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
而在
[DefNew: 7129K->520K(9216K), 0.0053010 secs] 7129K->6664K(19456K), 0.0053739 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
中我们会看到,刚分配的对象并没有被回收。
上面的GC是针对新生代的。
而下面的FullGC是针对老年代的。
如果我们这时候要再分配4m的内存,虚拟机默认将原先的eden区域放到可放的地方,也就是在老年代这里
因此会发生我们这种情况。
这就是整个过程。验证了对象有现在Eden区域回收
03 大对象直接进入到老年代
指定大对象的参数。
-XX:PretenureSizeThreshold
public class A {
private static int M = 1024*1024;
public static void main(String[] args) {
byte[] b1 = new byte[8*M];
}
}
测试代码:如下
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
运行结果如下:
Heap
我们可以看到,结果数直接把8M扔到了老年代里面了。
被发现7M全部扔到了eden里面。
如果我们制定了参数后,会发现结果变了。
参数如下所示:
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=6M
运行结果如下:
我们会发现7M进到了老年代。
04 长期存活对象进入老年代
参数如下:
-XX:MaxTenuringThreshold
每次进行回收的时候,如果没被回收,那对象的年龄+1
如果对象年龄到达阈值,就会进入老年代。
具体测试和上面的Max一样。就不占篇幅了。
05 空间分配担保
参数如下:
-XX:+HandlePromotionFailure
步骤如下:
首先衡量有没有这个能力,然后才能进行分配。
如果有这个能力放入,那么这个参数是‘+’号证明开启了内存担保,否则是‘-’号就是没开启。
逃逸分析与栈上分配
如何将内存分配到栈上呢?
逃逸分析的主要目标就是分析出对象的作用域。
总结,只要定义在方体中,对象的作用域不发生逃逸,否则发生逃逸。
06 总结
JVM内存分配策略不是特别复杂,只要一步一步跟着虚拟机走,那么就可以去理解JVM内存分配的机制。
![640?](https://i-blog.csdnimg.cn/blog_migrate/282c3ad013f9dbd5d0cf896b78ea0b20.gif)