第二章 JVM内存分配
注意:本篇博客,主要参考自以下四本书
《分布式Java应用:基础与实践》
《深入理解Java虚拟机(第二版)》
《突破程序员基本功的16课》
《实战java虚拟机》
说明:关于JVM内存结构,查看《第一章 JVM内存结构》,下面所讲的JVM内存分配主要是指在Hotspot JVM下新建对象在堆内存中分配的情况。
1、创建一个真正对象的基本过程
六步:
- 1. 类加载机制检查
- JVM首先检查一个new指令的参数是否能在常量池中定位到一个符号引用,并且检查该符号引用代表的类是否已被加载、解析和初始化过(实际上就是在检查new的对象所属的类是否已经执行过类加载机制)。如果没有,先进行类加载机制加载类。关于类加载机制,之后再说。
- 2. 分配内存
- 把一块儿确定大小的内存从Java堆中划分出来
- 3. 初始化对象
- 初始化零值(操作实例数据部分--对象内存布局三部分之一)
- 对象的实例字段不需要赋初始值也可以直接使用其默认零值,就是这里起得作用
- 每一种类型的对象都有不同的默认零值
- 设置对象头(操作对象头部分--对象内存布局三部分之一)
- 参看对象内存布局《附 Java对象内存布局》
- 执行<init>
- 为对象的字段赋值(在第三步只是初始化了零值,这里会根据所写程序给实例赋值)
- 初始化零值(操作实例数据部分--对象内存布局三部分之一)
- 4.将创建的对象指向分配的内存
注意:第三步和第四步可能发生指令重排序,这也是单例模式使用 volatile 修饰对象的原因。
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去拼接字符串,会形成许多临时字符串:如下,
String s1 = "hello1";
String s2 = "hello2";
String s3 = "hello3";
String s4 = s1+s2+s3;实际上,我们只想要字符串s1+s2+s3,但是在上述的拼接过程中,会形成s1+s2的临时字符串。拼接字符串,使用StringBuilder,该类相较于StringBuffer由于不是同步类,其运行效果会更好。
- 尽早释放无用对象的引用(帮助垃圾回收)
public void info(){
Object obj = new Object();
System.out.println(obj.hashCode());
obj = null;//显式释放无用对象
}如上边方法所示,其中的obj是一个局部变量,在方法执行结束后,栈帧就会出栈并被回收,栈帧中所存储的局部变量一起被回收掉了,所以这里的"obj=null;"就没用了,但是看下边
public void info(){
Object obj = new Object();
System.out.println(obj.hashCode());
obj = null;//显式释放无用对象
//下边还有一些很耗时、很耗内存的操作,这些操作与obj无关
}这时候,如果我们加上了"obj=null;"这一句,那么就有可能在方法执行结束之前,obj被回收。
- 尽量少使用static变量,因为static变量属于类变量,存储于方法区,其所占内存无法被垃圾回收器回收掉,除非static所属的类被卸载掉。
- 常用的对象放入缓存或连接池(其实,连接池也可以看做是一个缓存)
- 考虑使用SoftReference(关于几种引用方式,之后会说)
- 当内存足够时,功能等同于普通变量
- 当内存不足时,释放软引用所引用的对象
- 一般用于大数组、大对象
- 通过软引用所获取的对象可能为null(当内存不足时,释放软引用所引用的对象),在应用程序中需要显示判断对象是否为null,若为null,需要重建对象
第二章 JVM内存分配的更多相关文章
- Android性能调优篇之探索JVM内存分配
开篇废话 今天我们一起来学习JVM的内存分配,主要目的是为我们Android内存优化打下基础. 一直在想以什么样的方式来呈现这个知识点才能让我们易于理解,最终决定使用方法为:图解+源代码分析. 欢迎访 ...
- 第二章Java内存区域与内存溢出异常
第二章 Java内存区域与内存溢出异常 一.概述 对与Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每个new操作去写delete/free代码,不容易出现内存泄露和内存溢出问 题, ...
- 最简单例子图解JVM内存分配和回收
一.简介 JVM采用分代垃圾回收.在JVM的内存空间中把堆空间分为年老代和年轻代.将大量(据说是90%以上)创建了没多久就会消亡的对象存储在年轻代,而年老代中存放生命周期长久的实例对象.年轻代中又被分 ...
- JVM 内存分配模型概念和java中各种对象的存储
JVM 内存分配模型概念 --在工作中可能用到的机会不多,有个概念的了解 --此文是转载某位读者,应该是在阅读了<深入理解Java虚拟机JVM高级特性与最佳实践> 一书后,总结所得.写的不 ...
- 最简单例子图解JVM内存分配和回收(转)
本文转自http://ifeve.com/a-simple-example-demo-jvm-allocation-and-gc/ http://www.idouba.net/a-simple-exa ...
- 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常
第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域
- 第三章 JVM内存回收区域+对象存活的判断+引用类型+垃圾回收线程
注意:本文主要参考自<深入理解Java虚拟机(第二版)> 说明:查看本文之前,推荐先知道JVM内存结构,见<第一章 JVM内存结构> 1.内存回收的区域 堆:这是GC的主要区域 ...
- 虚拟机--第二章java内存区域与内存溢出异常--(抄书)
这是本人阅读周志明老师的<深入理解Java虚拟机>第二版抄写的,有很多省略,不适合直接阅读,需要阅读请出门左转淘宝,右转京东,支持周老师(侵权请联系删除) 第二章java内存区域与内存溢出 ...
- 浅谈JVM内存分配与垃圾回收
大家好,我是微尘,最近又去翻了周志明老师的<深入理解Java虚拟机>这本书.已经看了很多遍了,每次都感觉似乎看懂了,但没过多久就忘了.这次翻了第三章的垃圾收集器与内存分配策略,感觉有了新的 ...
随机推荐
- poj1730 - Perfect Pth Powers(完全平方数)(水题)
/* 以前做的一道水题,再做精度控制又出了错///... */ 题目大意: 求最大完全平方数,一个数b(不超过int范围),n=b^p,使得给定n,p最大: 题目给你一个数n,求p : 解题思路: 不 ...
- 分布式系统的烦恼------《Designing Data-Intensive Applications》读书笔记11
使用分布式系统与在单机系统中处理问题有很大的区别,分布式系统带来了更大的处理能力和存储容量之后,也带来了很多新的"烦恼".在这一篇之中,我们将看看分布式系统带给我们新的挑战. 1. ...
- 自然语言处理系列-1.什么是NLP?
常常会听到有人说,自然语言处理(NLP)是人工智能技术(AI)皇冠上的明珠.那么,从这句话上就能够看到,目前我们常常说的NLP其实是AI技术的一个分支,而且是较难的那一个分支. 那么,到底什么是NLP ...
- @Resource与@Autowired注解的区别(转)
Spring不但支持自己定义的@Autowired注解,还支持由JSR-250规范定义的几个注解.如:@Resource.@PostConstruct及@PreDestroy 1.@Autowired ...
- GPL、BSD和Apache开源许可证
参考资料 五种开源协议的比较(BSD,Apache,GPL,LGPL,MIT) 如何选择开源许可证? - 阮一峰的网络日志 开源许可证教程 - 阮一峰的网络日志 简介 自由软件许可证由FSF(Free ...
- python中的super( test, self).__init__()
python中的super( test, self).__init__() 对继承自父类的属性进行初始化 首先找到test的父类(比如是类A),然后把类test的对象self转换为类A的对象,然后“被 ...
- WinForm 使用 NPOI 2.2.1从datatable导出Excel
最新的NOPI应该是2.3了,但在官网上还是2.2.1. 也是第一次使用NPOI来导出Excel文件. 在写的时候搜不到2.2.1的教程,搜了一个2.2.0的教程. 不过也没什么问题,NPOI是真的方 ...
- 【BZOJ 3036】 3036: 绿豆蛙的归宿 (概率DP)
3036: 绿豆蛙的归宿 Time Limit: 2 Sec Memory Limit: 128 MBSubmit: 491 Solved: 354 Description 随着新版百度空间的下线 ...
- 【51nod-1239&1244】欧拉函数之和&莫比乌斯函数之和 杜教筛
题目链接: 1239:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1239 1244:http://www.51nod. ...
- Codeforces Round #288 (Div. 2) C. Anya and Ghosts 模拟 贪心
C. Anya and Ghosts time limit per test 2 seconds memory limit per test 256 megabytes input standard ...