2.JVM的分代模型及对象流动
2019-08-04 本文已影响0人
进读万卷书
1.为什么JVM要分代?
- 有些对象生活周期短。如下代码和图示:
public class MemoryAol {
public static void main(String[] args) throws InterruptedException {
while (true) {
loadUsers();
Thread.sleep(1000);
}
}
private static void loadUsers() {
UserUtil userUtil = new UserUtil();
userUtil.loadUsers();
}
}
1-8.png
如上所示的代码,main方法一直在运行,UserUtil对象会一直创建,但是当loadUsers执行完毕会进行出栈,此时年轻代堆里的刚创建的对象就没人引用了,这种对象当内存不足时是要被回收的,所以他们的存活周期很短。
- 有些对象要长期在内存里。 如下代码和图示。
public class MemoryAol {
private static UserUtil userUtil = new UserUtil();
public static void main(String[] args) throws InterruptedException {
while (true) {
loadUsers();
Thread.sleep(1000);
}
}
private static void loadUsers() {
userUtil.loadUsers();
}
}
1-9.png
如上代码,同样的,main方法一直在运行,不断地执行loadUsers,然后loadUsers又使用到userUtil,但这次不是局部变量,而是类的静态变量,引在存放在方法区,所以即使loadUsers方法不断地进栈出栈也不会对userUtil进行回收。随着程序不断运行,当它达到一定条件就会晋升到老年代,这时候这种对象成为了长期存活的对象了。
2.JVM是如何分代的?
JVM主要有如下三个分代:
- 年轻代
年轻代对象主要的特点是存活周期短,回收频率高,回收速度快,年轻代还分为Eden区,Survivor1区,Survivor2区,三者默认比例8:1:1。 - 年老代
年老代对象主要特点是存活周期长,回收频率低,回收速度慢,尽量减少老年代回收的频率及次数。 - 永久代
存放一些类信息及常量,当内存不足时同样也会进行回收,回收条件看下面。
3.什么时候触发新生代垃圾回收,永久代什么情况下会被回收?
-
新生代的回收时机
当新生代Eden区快满的时候(不足以存放新建对象)会触发新生代回收(minor GC),会有短暂的Stop the world。后续文章会详细说明回收机制,采用什么算法及垃圾回收器,以及优缺点等。 -
永久代回收时机
当进行Full GC的时候,同时会检查并回收永久代内存。永久代内存回收需满足以下全部条件:
1.该类的所有实例对象已经从JAVA堆内存里被回收。
2.加载这个类的ClassLoader已经被回收了。
3.该类的Class对象没有任何引用,比如User.class没有被引用。
4.对象在JVM是怎么分配和流动的?
- 新建对象放哪
新建对象放在年轻代的Eden区 - 大对象怎么办
大对象直接晋升老年代,通过-XX:PretenureSizeThreshold=<大小>,当大于设定大小的时候直接在老年代创建。 - 什么时候从年轻代升到老年代
每次发生minor gc,存活对象年龄加1,当年经代的对象年龄达到设置的大小,参数-XX:MaxTenuringThreshold设置,就会晋升到老年代。
5.JVM的参数设置
参数 | 例子 | 描述 |
---|---|---|
-Xms | -Xms2048m | java堆内存大小 |
-Xmx | -Xmx2048m | java最大堆内存大小,一般情况下和上面的设置一样 |
-Xmn | -Xmn1024m | 年轻代内存大小,扣除年轻代剩下的堆内存就是年老代的 |
-XX:PermSize | -XX:PermSize=256m | 永久代(方法区)内存大小 |
-XX:MaxPermSize | -XX:MaxPermSize=256m | 永久代最大内存大小 |
-Xss | -Xss1m | 每个线程栈的内存大小 |
画个图更加具体一些,如下图:
1-7.jpg