JVM深入了解,一些冷门容易被忽略的知识(Eden、Survivor、JDK8内存改动等)

提醒读者,以下所讲的内容不权威,仅值得参考,若有错误或者需要讨论的地方,请留言,我会认真查看回复。

本博客仅仅适合JVM初识者,适合作为一个小小的进阶。

如果你对JVM还没有任何了解的话,建议阅读:JVM内存分配初探

  • Stack:
    此区域会发生OOM,创建的线程占用的不是JVM的内存,而是OS分配给java进程的内存,所以,当请求分配的线程数量大于这个进程允许的最大线程数时,会发生OOM。
    最大线程数的一个计算公式:
    (MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
        MaxProcessMemory OS分配给java进程的最大内存
        JVMMemory         JVM内存
        ReservedOsMemory  保留的操作系统内存
        ThreadStackSize      线程栈的大小

    关于Stack的VM参数:
        -Xss512m 线程所占有的Stack栈大小

    调用的一个函数占一个栈帧,如果申请的栈帧数量大于Stack的深度,那么就会发生StackOverFlow异常:

    public void test(){
            test();
        }
    一旦调用这个test()函数就会发生StackOverFlow异常

  • Heap:
        还可以被分为新生代(Eden+Survivor1+Survivor2)、老生代(Tenured)、常量池(下面会介绍)。
    划分Heap区为以上新生代、老生代的一个理由就是为了GC的回收策略:分代回收。
        它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。
    对于常量池:
        在JDK1.6及之前,常量池是被划分在Perm Generation(持久代)中,但是JDK1.7及以后,就把常量池归到了Heap区,这段代码可以证明:(注:String.intern()方法是将Heap区的String对象实体复制到常量池,说白了就是往常量池中添加对象)

            int i = 0;
            List list = new ArrayList();
            while(true){
                list.add(String.valueOf(i++).intern());
            }

    JDK1.6:java.lang.OutOfMemoryError: PermGen space

    JDK1.7:java.lang.OutOfMemoryError: Java heap space


     所以,你可以说在JDK1.7及之后,Heap区分为:新生代(Eden+Survivor1+Survivor2)、老生代(Tenured)、常量池。

        

        新生代大小VM参数:-Xmn20m 。

        注此参数应当比-Xms -Xmx(heap区的默认值和最大值)要小,否则:
        设置VM参数:-Xms10m -Xmx20m -Xmn30m,运行java代码,得到警告:
        Java HotSpot(TM) 64-Bit Server VM warning: MaxNewSize (30720k) is equal to or greater than the entire heap (20480k).  A new max generation size of 19968k will be used. 

  • Permanent Generation()持久代
    有时候也称为方法区,在该区内很少发生垃圾回收,在这里进行的GC主要是方法区里的常量池和类的卸载
    方法区主要用来存储已被虚拟机加载的类信息、常量、静态变量和即时编译后的代码等数据
    方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。运行时生成的常量也会存在这个常量池中(当然,在JDK1.7及之后已经被划分到Heap区了)
    -XX:MaxPermSize设置上限
    -XX:PermSize设置最小值
        例:VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M

    但是,注意:JDK8中已经完全移除了永久带!
        所以说以上的参数-XX:MaxPermSize、-XX:PermSize已经完全没有用了。    
        在移除了Perm区域之后,JDK 8中使用MetaSpace来替代,对应的VM参数变成:
           -XX:MetaspaceSize=512m 初始值
           -XX:MaxMetaspaceSize=550m 最大值
    至于为什么有这个改动那就不是很清楚,还望有了解的人赐教。
  • Minor GC 和 Full GC
    Minor GC发生在Eden,Survivor1区,比较频繁,在Eden区朝生夕死的对象很多,当对象填满Eden区时候触发Minor GC。本次GC任然存活的对象被移到Survivor2。
    Full GC发生在老生代,老生代比例为新生代的1/8(一般),占用小,执行Full GC后任然存活的Survivor 2中的对象被移到老生代。
    详细推荐阅读

如有错误或者疑问,欢迎留言。

再次注明,本文所涉及内容不权威,仅值得参考。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值