转载请注明原文地址:https://blog.csdn.net/initphp/article/details/30487407

Java内存分配策略

使用的ParNew+Serial Old收集器组合

1. 优先在Eden上分配。

Java的对象优先会在新生代的Eden上分配。

我们可以看一个例子:

我设置了这些参数:-XX:+PrintGCDetails -Xms20m -Xmx20m -Xmn10m,堆内存分配20M,新生代10M,老生代10M,默认情况下Survivor区为8:1,所以Eden区域为8M

我运行这段代码:

  1. public class JavaTest {
  2. static int m = 1024 * 1024;
  3. public static void main(String[] args) {
  4. //分配2兆
  5. byte[] a1 = new byte[2 * m];
  6. System.out.println("a1 ok");
  7. //分配2兆
  8. byte[] a2 = new byte[2 * m];
  9. System.out.println("a2 ok");
  10. }
  11. }

控制台日志:

  1. a1 ok
  2. a2 ok
  3. Heap
  4. def new generation   total 9216K, used 4603K [0x331d0000, 0x33bd0000, 0x33bd0000)
  5. eden space 8192K,  56% used [0x331d0000, 0x3364ef50, 0x339d0000)
  6. from space 1024K,   0% used [0x339d0000, 0x339d0000, 0x33ad0000)
  7. to   space 1024K,   0% used [0x33ad0000, 0x33ad0000, 0x33bd0000)
  8. tenured generation   total 10240K, used 0K [0x33bd0000, 0x345d0000, 0x345d0000)
  9. the space 10240K,   0% used [0x33bd0000, 0x33bd0000, 0x33bd0200, 0x345d0000)
  10. compacting perm gen  total 12288K, used 381K [0x345d0000, 0x351d0000, 0x385d0000)
  11. the space 12288K,   3% used [0x345d0000, 0x3462f4d0, 0x3462f600, 0x351d0000)
  12. ro space 10240K,  55% used [0x385d0000, 0x38b51140, 0x38b51200, 0x38fd0000)
  13. rw space 12288K,  55% used [0x38fd0000, 0x396744c8, 0x39674600, 0x39bd0000)

日志中非常清晰的可以看到,我们分配了一个4M内存大小,直接是分配在了eden space里面。

2. 大对象直接进入老生代。

因为大对象一般是数组或者字符串,我们知道垃圾回收的算法是复制算法,所以需要大对象需要比较多的空间存储对象.在eden区间执行gc频率很高时会耗费很多性能,所以直接将大对象放入到老生代.

参数:-XX:PretenureSizeThreshold(该设置只对Serial和ParNew收集器生效) 可以设置进入老生代的大小限制,我们设置为3M,则大于3M的大对象就直接进入老生代

测试代码:

  1. public class JavaTest {
  2. static int m = 1024 * 1024;
  3. public static void main(String[] args) {
  4. //分配2兆
  5. byte[] a1 = new byte[2 * m];
  6. System.out.println("a1 ok");
  7. byte[] a3 = new byte[4 * m];
  8. System.out.println("a2 ok");
  9. }
  10. }

控制台日志:

  1. a1 ok
  2. a2 ok
  3. Heap
  4. def new generation   total 9216K, used 2555K [0x331d0000, 0x33bd0000, 0x33bd0000)
  5. eden space 8192K,  31% used [0x331d0000, 0x3344ef40, 0x339d0000)
  6. from space 1024K,   0% used [0x339d0000, 0x339d0000, 0x33ad0000)
  7. to   space 1024K,   0% used [0x33ad0000, 0x33ad0000, 0x33bd0000)
  8. tenured generation   total 10240K, used 4096K [0x33bd0000, 0x345d0000, 0x345d0000)
  9. the space 10240K,  40% used [0x33bd0000, 0x33fd0010, 0x33fd0200, 0x345d0000)
  10. compacting perm gen  total 12288K, used 381K [0x345d0000, 0x351d0000, 0x385d0000)
  11. the space 12288K,   3% used [0x345d0000, 0x3462f4d0, 0x3462f600, 0x351d0000)
  12. ro space 10240K,  55% used [0x385d0000, 0x38b51140, 0x38b51200, 0x38fd0000)
  13. rw space 12288K,  55% used [0x38fd0000, 0x396744c8, 0x39674600, 0x39bd0000)

上面的日志中,可以清洗看到第一次分配的2M留存在了eden space中,而4M超过了大对象设置的值3M,所以直接进入了老生代tenured generation

3. 长期存活的对象进入老年代

为了演示方便,我们设置-XX:MaxTenuringThreshold=1(默认15),当在新生代中年龄为1的对象进入老年代。

测试代码:

  1. public class JavaTest {
  2. static int m = 1024 * 1024;
  3. public static void main(String[] args) {
  4. //分配2兆
  5. byte[] a1 = new byte[1 * m / 4];
  6. System.out.println("a1 ok");
  7. byte[] a2 = new byte[7 * m];
  8. System.out.println("a2 ok");
  9. byte[] a3 = new byte[3 * m]; //GC
  10. System.out.println("a3 ok");
  11. }
  12. }

控制台日志:

  1. a1 ok
  2. a2 ok
  3. [GC [DefNew: 7767K->403K(9216K), 0.0062209 secs] 7767K->7571K(19456K), 0.0062482 secs]
  4. [Times: user=0.00 sys=0.00, real=0.01 secs]
  5. a3 ok
  6. Heap
  7. def new generation   total 9216K, used 3639K [0x331d0000, 0x33bd0000, 0x33bd0000)
  8. eden space 8192K,  39% used [0x331d0000, 0x334f9040, 0x339d0000)
  9. from space 1024K,  39% used [0x33ad0000, 0x33b34de8, 0x33bd0000)
  10. to   space 1024K,   0% used [0x339d0000, 0x339d0000, 0x33ad0000)
  11. tenured generation   total 10240K, used 7168K [0x33bd0000, 0x345d0000, 0x345d0000)
  12. the space 10240K,  70% used [0x33bd0000, 0x342d0010, 0x342d0200, 0x345d0000)
  13. compacting perm gen  total 12288K, used 381K [0x345d0000, 0x351d0000, 0x385d0000)
  14. the space 12288K,   3% used [0x345d0000, 0x3462f548, 0x3462f600, 0x351d0000)
  15. ro space 10240K,  55% used [0x385d0000, 0x38b51140, 0x38b51200, 0x38fd0000)
  16. rw space 12288K,  55% used [0x38fd0000, 0x396744c8, 0x39674600, 0x39bd0000)

我们可以看到在A3处有一次GC,并且a2的7M已经满足-XX:MaxTenuringThreshold=1的要求,所以a2进入老年代,而空出来的空间a3就进入新生代

4. 动态对象年龄判定

为了使内存分配更加灵活,虚拟机并不要求对象年龄达到MaxTenuringThreshold才晋升老年代

如果Survivor区中相同年龄所有对象大小的总和大于Survivor区空间的一半,年龄大于或等于该年龄的对象在Minor GC时将复制至老年代

5. 空间分配担保

新生代使用复制算法,当Minor GC时如果存活对象过多,无法完全放入Survivor区,就会向老年代借用内存存放对象,以完成Minor GC。

在触发Minor GC时,虚拟机会先检测之前GC时租借的老年代内存的平均大小是否大于老年代的剩余内存,如果大于,则将Minor GC变为一次Full GC,如果小于,则查看虚拟机是否允许担保失败,如果允许担保失败,则只执行一次Minor GC,否则也要将Minor GC变为一次Full GC。

说白了,新生代放不下就会借用老年代的空间来进行GC

深入理解java虚拟机---内存分配策略(十三)的更多相关文章

  1. 深入理解Java虚拟机-内存分配与回收策略

    一.内存分配策略 新生代中98%的对象都是"朝生夕死"的,所以并不需要按照1:1的比例来划分内存空间,而是将内存(新生代内存)分为一块较大的Eden(伊甸园)空间和两块较小的Sur ...

  2. 小白请教几个关于Java虚拟机内存分配策略的问题

    最近在看周志明所著的<深入理解Java虚拟机>,有几个问题不太明白,希望对虚拟机有研究的哥们儿帮我解答一下.先说一下我进行试验的环境: 操作系统:Mac OS X 10.11.6 EI C ...

  3. 理解java虚拟机内存分配堆,栈和方法区

    栈:存放局部变量 堆:存放new出来的对象 方法区:存放类的信息,static变量,常量池(字符串常量) 在堆中,可以说是堆的一部分   创建了一个student类,定义了name属性, id静态变量 ...

  4. java中内存分配策略及堆和栈的比较

    Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...

  5. JAVA虚拟机内存分配与回收机制

    Java虚拟机(Java Virtual Machine) 简称JVM Java虚拟机是一个想象中的机器,在实际的计算机上通过软件模拟来实现.Java虚拟机有自己想象中的硬件,如处理器.堆栈.寄存器等 ...

  6. Java虚拟机内存分配与回收策略

    内存分配与回收策略 Minor GC 和 Full GC Minor GC:发生在新生代上,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行, 执行的速度一般也会比较快. Full GC ...

  7. Java虚拟机内存分配详解

    简介 了解Java虚拟机内存分布的好处 1.了解Java内存管理的细节,有助于程序员编写出性能更好的程序.比如,在新的线程创建时,JVM会为每个线程创建一个专属的栈 (stack),其栈是先进后出的数 ...

  8. 深入理解Java虚拟机内存模型

    前言 本文中部分内容引用至<深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)>第12章,如果有兴趣可自行深入阅读,文末放有书籍PDF版本连接. 一.物理机中的并发 物理机遇到的并 ...

  9. 深入理解Java虚拟机—内存管理机制

    前面说过了类的加载机制,里面讲到了类的初始化中时用到了一部分内存管理的知识,这里让我们来看下Java虚拟机是如何管理内存的. 先让我们来看张图 有些文章中对线程隔离区还称之为线程独占区,其实是一个意思 ...

随机推荐

  1. 关于Python中的ifelse、while和for循环的简单小结

    1.ifelse 1.1首先简单编辑一个关于ifelse的程序: _username = 'yanfeixu' _password = 'wuyifan' username = input(" ...

  2. 猫眼电影爬取(三):requests+pyquery,并将数据存储到mysql数据库

    还是以猫眼电影为例,这次用pyquery库进行爬取 1.简单demo,看看如何使用pyquery提取信息,并将提取到的数据进行组合 # coding: utf-8 # author: hmk impo ...

  3. GrindEQ Math Utilities 2015破解版 图文安装和序列号补丁激活教程

    GrindEQ Math Utilities 2015破解版 图文安装和序列号补丁激活教程 https://www.sdbeta.com/mf/2018/1002/226048.html 软件下载: ...

  4. 【转】 多线程之linux线程调度策略

    转自:http://blog.csdn.net/byperseverance/article/details/44522731 Linux线程的调度策略分为3个:SCHED_OTHER,SCHED_F ...

  5. 上传RNA-seq数据到NCBI GEO数据库

    SRA - NCBI example - NCBI 要发文章了,审稿时编辑肯定会要求你上传NGS测序数据. 一般数据都是放在集群,不可能放在个人电脑上,因为有的数据大的吓人(几个T). 所以我们就建一 ...

  6. 基因/转录本/任意特征 表达定量工具之featureCounts使用方法 | 参数详解

    featureCounts真的很厉害. 常见的参数(没什么好说的,毕竟是固定的): -a -o input_file1 -F -t -g -Q -T 关键是以下几个参数怎么设置: -f # Perfo ...

  7. java ----> 手动编译java项目

    环境: jdk1.8,cmd,notepad++ 创建java工程test,创建文件夹: src classes lib 说明: src 放置.java文件 classes 放置.class文件 li ...

  8. IntelliJ IDEA 安装 Scala 插件

    本页面中对在 IntelliJ 中安装 Scala 插件的步骤和方法进行了描述. 需要在 IntelliJ  安装 Scala 插件,你首先需要在你的计算机中安装 IntelliJ .IntelliJ ...

  9. Confluence 6 给一个从 Jira Service Desk 的非许可证用户访问权限

    如果你正在使用 Confluence 为 Jira 服务桌面(Jira Service Desk)的知识库,你可以选择允许所有活动的用户和客户(客户是可以登录的用户,但是这些用户是没有 Conflue ...

  10. Stark组件 (一)

    Stark组件构建 1.启动所有app下的stark.py文件,的配置实现步骤 1.创建一个Django项目crm,并创建  app1 ,app2, stark 三个app 2.在crm 项目的set ...