zoukankan      html  css  js  c++  java
  • 对象创建与堆

    这一节主要介绍对象创建时,在堆中的一些过程。

    回忆下,我们之前说的,什么时候会发生垃圾回收?

    除了在一些安全点处也许会发生垃圾回收(只是也许),如果在所需内存不足的情况下,一定会发生垃圾回收。

    分配堆空间

    首先通过设置参数,把堆空间设置为 20M,其中 新生代 10M,老年代 10M。

    参数设置:

    -Xms20m -Xmx20m -Xmn10m -XX:+PrintGCDetails

    结果为:

    Heap
     PSYoungGen      total 9216K, used 1685K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
      eden space 8192K, 20% used [0x00000007bf600000,0x00000007bf7a5580,0x00000007bfe00000)
      from space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
      to   space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
     ParOldGen       total 10240K, used 0K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      object space 10240K, 0% used [0x00000007bec00000,0x00000007bec00000,0x00000007bf600000)
    

    创建一个新对象

    我们首先创建一个对象,这个对象占用 2M 的空间。

    package heap;
    
    public class CreateObject {
        public static void main(String[] args) {
            byte[] obj1 = new byte[1024 * 1024 * 2];
        }
    }
    

    最后的输出:

    Heap
     PSYoungGen      total 9216K, used 3733K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
      eden space 8192K, 45% used [0x00000007bf600000,0x00000007bf9a5590,0x00000007bfe00000)
      from space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
      to   space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
     ParOldGen       total 10240K, used 0K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
      object space 10240K, 0% used [0x00000007bec00000,0x00000007bec00000,0x00000007bf600000)
    

    可以看到,新生代 被占用了,老年代占用为 0K,没有被使用。

    所以,new 的对象先放在 eden 区。

    填满 eden 区

    在填满 eden 区后,会发生什么呢?因为 survivor 区实在太小了,很难看到。所以,这里可以借助 Visual VM,来观察,更加直观。

    程序如下:

    package heap;
    
    public class CreateObject {
        public static void main(String[] args) {
            while(true){
                byte[] bytes = new byte[1024 * 512];
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    

    重点是看右边的记录图。注意,这里我们将每次创建对象的大小设置为了 0.5M。

    当 Eden 满的时候,会调用垃圾回收器,调用垃圾回收器后,Eden 出现了低谷,Survivor 出现了一个增长。老年区也出现了一个增长。

    当 Eden 满的时候,如果 Survivor 区有足够的空间容纳存活对象,那么可以把存活对象放入 Survivor,多的对象放入老年区。

    现在,我们把对象的大小调大。设置为 2M,这样 Survivor 就无法存放下。

    可以看到,在经过一次垃圾回收的时候(可以看到GC Time 上有波峰,说明执行了一次垃圾回收),但我们注意到,Survivor 区中并没有被占用。说明垃圾回收过程中,直接将存活对象放到了老年代中。

    再来聊聊 survivor 区

    对象通常在 Eden 区里诞生,如果经过第一次 MInor GC 后仍然存活,并且能够被 Survivor 容纳的话,该对象会被移动到 Survivor 区,并且将其年龄设置为 1 岁。对象在 Survivor 区每熬过一次 Minor GC,年龄就增加 1 岁,当它年龄增大到一定程度(默认是 15 岁),就会被晋升到老年代。

    特殊情况

    有些时候,如果用户创建了大对象,如很长的字符串或者元素很多的数组的时候。这种大对象都占用大量的内存,像这种大对象,有很大概率是长时间使用的,不然为什么要创建大对象。

    如果大对象朝生夕灭,我们知道在 Java 8 中,新生代默认采用的 标记-复制 算法,那么对于大对象而言,是非常耗时的。

    所以,如果 JVM 设置了一个阈值,那么当分配的对象大于这个阈值的时候,会直接被分配到老年代。

    总结

  • 相关阅读:
    java
    Java 自定义异常(转载)
    java中更新文件时,指定原文件的编码格式,防止编码格式不对,造成乱码
    tar命令压缩和解压
    微服务之服务注册与发现--Consul(转载)
    git push 时:报missing Change-Id in commit message footer的错误
    git 版本回退
    item 快捷键
    mac下mysql的卸载和安装
    JAVA正则表达式:Pattern类与Matcher类详解(转)
  • 原文地址:https://www.cnblogs.com/zhouzhiyao/p/13179805.html
Copyright © 2011-2022 走看看