今天看了下Jvm中新生代和老年代的定义和解析,然后看到一篇博,把自己整懵了,还好后面应该是懂了。

  • 首先是我看到的关于新生代和老年代相关的部分

3.1.1 新生代:

分为三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1。一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。

因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。

在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。(动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。)经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

GC可分为三种:Minor GC Major GC 和 Full GC

Minor GC :是清理新生代。触发条件:当Eden区满时,触发Minor GC。

Major GC:是清理老年代。是 Major GC 还是 Full GC,大家应该关注当前的 GC 是否停止了所有应用程序的线程,还是能够并发的处理而不用停掉应用程序的线程

Full GC :是清理整个堆空间—包括年轻代和老年代。触发条件:调用System.gc时,系统建议执行Full GC,但是不必然执行;老年代空间不足;方法区空间不足;通过Minor GC后进入老年代的平均大小大于老年代的可用内存;由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

3.1.2 老年代:

主要存放应用程序中生命周期长的内存对象。

老年代的对象比较稳定,所以MajorGC不会频繁执行。在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。

MajorGC采用标记—清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。

当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

  • 让我一开始没有看明白的文章部分代码
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.util.ArrayList;
import java.util.List;

public class TestJvm {
    byte[] a = new byte[1024*1024*1];//1M

    public static void main(String[] args) {
            List<Object> list = new ArrayList<Object>();
            int i =1;
            MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
            System.out.println("初始化堆的当前内存使用量:"+memoryMXBean.getHeapMemoryUsage());
            System.out.println("初始化非堆内存的当前内存使用量:"+memoryMXBean.getNonHeapMemoryUsage());
            while (true){
                list.add(new TestJvm());
                try {
                    Thread.sleep(40);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
//            System.out.println("非堆内存的当前内存使用量:"+memoryMXBean.getNonHeapMemoryUsage());
                System.out.println("堆内存的当前内存使用量:"+memoryMXBean.getHeapMemoryUsage());
                System.out.println(i++);
            }
        }

    }
  • 解析

程序是个死循环,每次运行TestJvm占用内存1m,我主要是想看看这个数据是怎么在新生代和老年代之间走的,下面是输出:

初始化堆的当前内存使用量:init = 31457280(30720K) used = 976008(953K) committed = 30146560(29440K) max = 30146560(29440K)
初始化非堆内存的当前内存使用量:init = 23527424(22976K) used = 4016128(3922K) committed = 23527424(22976K) max = 71303168(69632K)
堆内存的当前内存使用量:init = 31457280(30720K) used = 2190088(2138K) committed = 30146560(29440K) max = 30146560(29440K)
1
堆内存的当前内存使用量:init = 31457280(30720K) used = 3238680(3162K) committed = 30146560(29440K) max = 30146560(29440K)
2
堆内存的当前内存使用量:init = 31457280(30720K) used = 4287272(4186K) committed = 30146560(29440K) max = 30146560(29440K)
3
堆内存的当前内存使用量:init = 31457280(30720K) used = 5335864(5210K) committed = 30146560(29440K) max = 30146560(29440K)
4
堆内存的当前内存使用量:init = 31457280(30720K) used = 6384456(6234K) committed = 30146560(29440K) max = 30146560(29440K)
5
堆内存的当前内存使用量:init = 31457280(30720K) used = 7433048(7258K) committed = 30146560(29440K) max = 30146560(29440K)
6

到这里为止还好,我还能明白,占用的数据都在新生代的Eden区,新生代分为3个区:Eden区和名为“From”的Survivor区,“To”的Survivor区

[GC [PSYoungGen: 7258K->1248K(8960K)] 7258K->6460K(29440K), 0.0047718 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
堆内存的当前内存使用量:init = 31457280(30720K) used = 7908112(7722K) committed = 30146560(29440K) max = 30146560(29440K)
7

然后开始新生代GC,新生代已使用7258k,GC之后新生代已使用1248k,新生代总大小9M,GC前Java堆已使用容量7258K,GC后Java堆已使用容量6460K,总堆的大小29M。

Eden区中所有存活的对象都会被复制到“To”,结果就是在To,放不下的进入了老年代。

经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。也就是Full CG

上面都是1M1次增加占用,垃圾回收以后变成增加了0.5M,我都对象都在使用中,不可能会被垃圾释放掉,我的这些内存跑哪里区了呢?

最后我只能认为,真的是释放了点垃圾吧,具体什么垃圾我是真的没有想明白。 

堆内存的当前内存使用量:init = 31457280(30720K) used = 8956704(8746K) committed = 30146560(29440K) max = 30146560(29440K)
8
堆内存的当前内存使用量:init = 31457280(30720K) used = 10005296(9770K) committed = 30146560(29440K) max = 30146560(29440K)
9
堆内存的当前内存使用量:init = 31457280(30720K) used = 11053888(10794K) committed = 30146560(29440K) max = 30146560(29440K)
10
堆内存的当前内存使用量:init = 31457280(30720K) used = 12102480(11818K) committed = 30146560(29440K) max = 30146560(29440K)
11
堆内存的当前内存使用量:init = 31457280(30720K) used = 13151072(12842K) committed = 30146560(29440K) max = 30146560(29440K)
12
堆内存的当前内存使用量:init = 31457280(30720K) used = 14199664(13866K) committed = 30146560(29440K) max = 30146560(29440K)
13
[GC [PSYoungGen: 8654K->1248K(8960K)] 13866K->13628K(29440K), 0.0014215 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
堆内存的当前内存使用量:init = 31457280(30720K) used = 15061144(14708K) committed = 30146560(29440K) max = 30146560(29440K)
14
堆内存的当前内存使用量:init = 31457280(30720K) used = 16109736(15732K) committed = 30146560(29440K) max = 30146560(29440K)
15
堆内存的当前内存使用量:init = 31457280(30720K) used = 17158328(16756K) committed = 30146560(29440K) max = 30146560(29440K)
16
堆内存的当前内存使用量:init = 31457280(30720K) used = 18206920(17780K) committed = 30146560(29440K) max = 30146560(29440K)
17
堆内存的当前内存使用量:init = 31457280(30720K) used = 19255512(18804K) committed = 30146560(29440K) max = 30146560(29440K)
18
堆内存的当前内存使用量:init = 31457280(30720K) used = 20304104(19828K) committed = 30146560(29440K) max = 30146560(29440K)
19
堆内存的当前内存使用量:init = 31457280(30720K) used = 21352696(20852K) committed = 30146560(29440K) max = 30146560(29440K)
20
[GC [PSYoungGen: 8472K->1200K(8960K)] 20852K->20748K(29440K), 0.0015864 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC [PSYoungGen: 1200K->1040K(8960K)] [PSOldGen: 19548K->19663K(20480K)] 20748K->20703K(29440K) [PSPermGen: 3887K->3887K(20480K)], 0.0250062 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] 
堆内存的当前内存使用量:init = 31457280(30720K) used = 22443752(21917K) committed = 30146560(29440K) max = 30146560(29440K)
21
堆内存的当前内存使用量:init = 31457280(30720K) used = 23492344(22941K) committed = 30146560(29440K) max = 30146560(29440K)
22
堆内存的当前内存使用量:init = 31457280(30720K) used = 24540936(23965K) committed = 30146560(29440K) max = 30146560(29440K)
23
堆内存的当前内存使用量:init = 31457280(30720K) used = 25589528(24989K) committed = 30146560(29440K) max = 30146560(29440K)
24
堆内存的当前内存使用量:init = 31457280(30720K) used = 26638120(26013K) committed = 30146560(29440K) max = 30146560(29440K)
25
堆内存的当前内存使用量:init = 31457280(30720K) used = 27686712(27037K) committed = 30146560(29440K) max = 30146560(29440K)
26
[Full GC [PSYoungGen: 7374K->7374K(8960K)] [PSOldGen: 19663K->19663K(20480K)] 27037K->27037K(29440K) [PSPermGen: 3889K->3889K(20480K)], 0.0034263 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC [PSYoungGen: 7374K->7184K(8960K)] [PSOldGen: 19663K->19634K(20480K)] 27037K->26818K(29440K) [PSPermGen: 3889K->3881K(20480K)], 0.0091912 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
java.lang.OutOfMemoryError: Java heap space
Dumping heap to C:\Users\Administrator\Desktop\error_logs ...
Unable to create C:\Users\Administrator\Desktop\error_logs: No such file or directory
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at TestJvm.<init>(TestJvm.java from InputFileObject:7)
    at TestJvm.main(TestJvm.java from InputFileObject:16)
Heap
 PSYoungGen      total 8960K, used 7285K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 7680K, 94% used [0x00000000ff600000,0x00000000ffd1d640,0x00000000ffd80000)
  from space 1280K, 0% used [0x00000000ffd80000,0x00000000ffd80000,0x00000000ffec0000)
  to   space 1280K, 0% used [0x00000000ffec0000,0x00000000ffec0000,0x0000000100000000)
 PSOldGen        total 20480K, used 19634K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 95% used [0x00000000fe200000,0x00000000ff52c820,0x00000000ff600000)
 PSPermGen       total 20480K, used 3890K [0x00000000fce00000, 0x00000000fe200000, 0x00000000fe200000)
  object space 20480K, 18% used [0x00000000fce00000,0x00000000fd1cca28,0x00000000fe200000)