你的位置:首页 > 软件开发 > Java > 第二章 JVM内存分配

第二章 JVM内存分配

发布时间:2016-02-03 21:00:10
注意:本篇博客,主要参考自以下三本书《分布式Java应用:基础与实践》《深入理解Java虚拟机(第二版)》《突破程序员基本功的16课》说明:关于JVM内存结构,查看《第一章 JVM内存结构》,下面所讲的JVM内存分配主要是指在Hotspot JVM下新建对象在堆内存中分配的情况。 ...

注意:本篇博客,主要参考自以下三本书

《分布式Java应用:基础与实践》

《深入理解Java虚拟机(第二版)》

《突破程序员基本功的16课》

说明:关于JVM内存结构,查看《第一章 JVM内存结构》,下面所讲的JVM内存分配主要是指在Hotspot JVM下新建对象在堆内存中分配的情况。

 

1、创建一个真正对象的基本过程

五步:

  • 类加载机制检查
    • JVM首先检查一个new指令的参数是否能在常量池中定位到一个符号引用,并且检查该符号引用代表的类是否已被加载、解析和初始化过(实际上就是在检查new的对象所属的类是否已经执行过类加载机制)。如果没有,先进行类加载机制加载类。关于类加载机制,之后再说。
  • 分配内存
    • 把一块儿确定大小的内存从Java堆中划分出来
  • 初始化零值(操作实例数据部分--对象内存布局三部分之一)
    • 对象的实例字段不需要赋初始值也可以直接使用其默认零值,就是这里起得作用
    • 每一种类型的对象都有不同的默认零值
  • 设置对象头(操作对象头部分--对象内存布局三部分之一)
    • 这一块儿在对象的内存布局中说
  • 执行<init>
    • 为对象的字段赋值(在第三步只是初始化了零值,这里会根据所写程序给实例赋值)

 

2、内存分配概念

  • 在类加载完成后,一个对象所需的内存大小就可以完全确定了,具体的情况查看对象的内存布局。
  • 为对象分配空间,即把一块儿确定大小(上述确定下来的对象内存大小)的内存从Java堆中划分出来

 

3、内存分配两种方式

  • 指针碰撞
    • 适用场合:堆内存规整(即没有内存碎片)的情况下
    • 原理:用过的内存全部整合到一边,没有用过的内存放在另一边,中间有一个分界值指针,只需要向着没用过的内存方向将该指针移动对象内存大小位置即可
    • GC收集器:Serial、ParNew
  • 空闲列表
    • 适用场合:堆内存不规整的情况下
    • 原理:虚拟机会维护一个列表,该列表中会记录哪些内存块是可用的,在分配的时候,找一块儿足够大的内存块儿来划分给对象实例(这一块儿可以类比memcached的slab模型),最后更新列表记录。关于memcached slab内存模型介绍查看《第六章 memcached剖析》
    • GC收集器:CMS
  • 注意
    • 选择以上两种方式中的哪一种,取决于Java堆内存是否规整
    • Java堆内存是否规整,取决于GC收集器的算法是"标记-清除",还是"标记-整理"(也称作"标记-压缩")

 

4、内存分配并发问题

堆内存是各个线程的共享区域,所以在操作堆内存的时候,需要处理并发问题。处理的方式有两种:

  • CAS+失败重试
    • 具体的做法与AtomicInteger的getAndSet(int newValue)方法的实现方式类似,关于AtomicInteger的源码解析,查看《第十一章 AtomicInteger源码解析》
  • TLAB(Thread Local Allocation Buffer)
    • 原理:为每一个线程预先在Eden区分配一块儿内存,JVM在给线程中的对象分配内存时,首先在TLAB分配,当对象大于TLAB中的剩余内存或TLAB的内存已用尽时,再采用上述的CAS进行内存分配
    • -XX:+/-UseTLAB:是否使用TLAB
    • -XX:TLABWasteTargetPercent设置TLAB可占用的Eden区的比率,默认为1%
    • JVM会根据以下三个条件来给每个线程分配合适大小的TLAB
      • -XX:TLABWasteTargetPercent
      • 线程数量
      • 线程是否频繁分配对象
    • -XX:PrintTLAB:查看TLAB的使用情况

 

5、总结

  • 尽量少创建对象
    • 根据第一块儿所说,创建一个对象的过程比较复杂,耗时较多,所以尽量减少对象的创建
    • 对象创建的少,将来垃圾收集器收集的垃圾也就少,提高效率
    • 对象创建的少,占用内存也就少,那么剩余的系统内存也就相对多,系统运行也就快
    • 避免在经常使用的方法中或循环中创建对象
  • 多个小的对象比大对象分配起来更加高效
    • 这是根据TLAB得出来的,多个小对象可以并行在各自的TLAB分配内存,而大对象的话,可能只能通过CAS同步来分配内存
  • 衡量上述两点
  • 对于String
    • 尽量使用直接量:eg. String str = "hello";//常量会直接存在"常量池",而非String str = new String("hello");//除了将"hello"存在"常量池"之外,还会创建一个char[]
    • 不要使用String去拼接字符串,会形成许多临时字符串:如下,第二章 JVM内存分配第二章 JVM内存分配
        String s1 = "hello1";  String s2 = "hello2";  String s3 = "hello3";  String s4 = s1+s2+s3;

       

      海外公司注册、海外银行开户、跨境平台代入驻、VAT、EPR等知识和在线办理:https://www.xlkjsw.com

      原标题:第二章 JVM内存分配

      关键词:jvm

jvm
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。