Java_内存
来源:互联网 发布:热血江湖衣服强化数据 编辑:程序博客网 时间:2024/05/21 03:55
Java_内存
本文由 Luzhuo 编写,转发请保留该信息.
原文: http://blog.csdn.net/Rozol/article/details/78050885
运行时数据区域分为: 方法区 / 堆 / Java栈 / Native方法栈 / 程序计数器
内存 判断 / 回收 算法
新生代内存划分为 Eden[‘i:dn] 和 Survivor[sərˈvaɪvə(r)] * 2 区
JDK8以后, 永久代被彻底移除, 用元空间
代替; 元空间不在JVM中, 而是使用本地内存
Java内存管理
运行时数据区域
方法区
- 存放被已被加载的类信息 / 常量 / 静态方法 / 字符串常量池 等信息
- 当方法区无法申请到足够的内存时, 将抛出OutOfMemoryError异常
- JDK8以后, 永久代已被从方法区彻底移除, 用
元空间(Metaspace)
代替; 元空间不在JVM中, 而是使用本地内存- 方法区是概念, 永久代是方法区的实现, 永久代被移除方法区并不表示方法区没有了
- 使用本地内存的元空间, 避免了不确定大小的类和字符串的加载导致内存溢出
堆
- 所有被
new出来的实例对象
和数组
都在堆上分配内存 - 堆是GC管理的主要区域
- 所有被
Java栈
- 每个方法的
调用
到执行完成
, 都对应着一个栈在Java栈中入栈
到出栈
的过程 - 当前线程请求的最大
栈的深度
超过所设定的深度(可设置为动态扩展), 将抛出StackOverflowError异常 - 当前栈的最大深度为可动态扩展时, 如果扩展时无法申请到足够的内存, 将抛出OutOfMemoryError异常
- 每个方法的
本地方法栈
- 与Java栈类似, 只不过这里执行的Native方法不限语言, 不限数据类型
程序计数器
- 用来记录当前线程的
字节码执行位置
的 - 当线程被切换后需要恢复到正确的执行位置, 所以每个线程都会有个独立的程序计数器
- 用来记录当前线程的
本地内存
- 这部分内存不属于
运行时数据区
的范围 - 比如JDK4的NIO, 他可以通过Native函数库直接分配对外内存, 然后通过Java堆中的DirectByteBuffer对象操作该内存的引用, 以提高性能
- 超出可用物理内存限制, 将抛OutOfMemoryError异常
- 这部分内存不属于
垃圾回收器GC
哪些内存需要被回收
- 已经不再被任何途径使用的对象需要被回收
- Java采用 可达性分析算法 判定某对象是否可回收
Java中还有对象缓存功能, 对一些对象只在内存紧张时才进行回收
强引用
GC永远不会回收掉该引用的对象
Object obj = new Object();
软引用
一些还有用但非必需的对象, 内存将要溢出之前, 对这些对象进行回收
Object obj = new Object();SoftReference<Object> softRef = new SoftReference<Object>(obj); // 软引用if (softRef != null) { // 软引用可能已被回收 obj = softRef.get(); // 获取软引用中的对象}
弱引用
一些非必需的对象, 不管内存是否足够, 都对这些内存进行回收
Object obj = new Object();WeakReference<Object> weakRef = new WeakReference<Object>(obj); // 弱引用if (weakRef != null) { // 弱引用可能已被回收 obj = weakRef.get(); // 获取弱引用中的对象}
虚引用
虚引用完全不会对期生存时间造成影响, 也无法通过虚引用获取该对象, 但是在被GC时可收到通知
Object obj = new Object();ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>(); // 引用队列PhantomReference<Object> phantomRef = new PhantomReference<Object>(obj, refQueue); // 虚引用System.out.println("refQueue: " + refQueue.poll()); // => null// GC 发现虚引用, 会将 phantomRef 插入到 refQueue 队列, 此时 obj 未被回收// GC 第二次发现虚引用, phantomRef 插入到 refQueue 队列失败, 才会对 obj 进行回收obj = null;System.gc();Thread.sleep(1000);System.out.println("refQueue: " + refQueue.poll()); // => java.lang.ref.PhantomReference@15db9742
finalize() 方法:
- 如果对象在 finalize() 方法中重新与引用链上的任何一个对象建立关联, 那么该对象会在第二次标记是被移除回收集合, 就能存活下来
- 由于 finalize() 只会被调用一次, 用过之后下次在触发回收, finalize() 不会被执行, 也就被kill了
- 所以不建议复写该方法
常见的几种判断对象是否存活的算法:
引用计数算法
- 给对象添加一个引用计数器, 每当一个地方引用它, 则计数器+1, 每当引用失效, 则计数器-1
当两个对象相互引用时, 即使这两个对象不再被访问, 但是他们相互引用导致计数器不为0, 所以他们无法被回收
objA.instance = objBobjB.instance = objA
所以Java不会采用该算法来管理内存
可达性分析算法
- 以
G Roots
为起始点, 向下搜索, 任何与 GC Roots 有引用链(能经过的路径称为引用链)相连的对象, 则判定该存活的对象, 否则判定为可回收的对象 - Java就是采用该算法来管理内存
- 以
什么时候回收
- 对象优先在
新生代Eden区
中分配, 当Eden区没有足够内存时, 将执行一次新生代GC.- 新生代采用
复制算法
收集内存
- 新生代采用
- 大对象(需要大量连续空间的对象, 如String, Array[])直接进入老年代.
- 经常出现大对象容易造成提前触发GC
- 所以尽量避免短命的大对象
- 长期存活的对象将进入老年代
- 对象在Eden出生并经历一次新生代GC仍存活,计数1, 将移到新生代Survivor区, 每次经历Survivor区一次新生代GC,计数+1, 当该对象计数≥15时, 将进入老年代.
- 当新生代Survivor空间中所有相同计数的对象总大小大于Survivor空间1/2时, 计数≥该计数的对象直接进入老年代
- 在执行新生代GC前, 计算是否是安全的GC(老年代可用空间≥新生代所有对象大小 || 老年代可用空间≥平均晋升大小)
- 安全的, 将执行一次新生代GC
- 不安全的, 将执行一次全部GC (full gc)
- jdk6之后, 是否允许(新生代GC执行)失败参数将不再起作用
- 流程图
如何回收
- 复制 算法: 用于新生代
- 容量比例为: Eden : Survivor = 8 : 1*2
- 新生代内存按比例划分成一块Eden和两块Survivor
- 每次使用Eden和其中一块Survivor, 当回收时, 将Eden和Survivor中还存活的对象复制到另一块Survivor上, 然后把Eden和已用的Survivor空间一次清理掉. (每次都是整区回收, 也就无需考虑碎片问题)
- 如果发生Survivor内存不够用时, 需要老年代内存进行分配担保
- 示范图
- 标记-整理 算法: 用于老年代
- 在老年代, 存活的对象较多, 如果使用
复制算法
, 效率会变低, 这就需要标记-整理
算法 - 分为”标记”和”清理”两个阶段
- 标记: 标记处所有存活的对象
- 整理: 让所有存活的对象向一端移动, 然后直接清理掉端边界以外的空间
- 示范图
- 在老年代, 存活的对象较多, 如果使用
内存中的数据传递案例图
内存优化
工具使用
命令
- jps: 所有HotSpot虚拟机进程的LVMID
- 格式:
jps [options] [hostid]
- options:
- -q: 只输出虚拟机进程的LVMID
- -m: 进程启动时传给main()的参数
- -l: 输出全类名
- -v: JVM参数
- hostid: RMI注册表中注册的主机名
- 示范:
jps -l
- 格式:
- jstat: 监视JVM运行状态信息
- 格式:
jstat [ option vmid [interval [s|ms] [count]] ]
- option
- -class: 类装载数量 / 类卸载数量 / 总大小 / 装载耗时
- -gc: Java堆状况(Eden区:E / Survivor区:S / 老年代:O / 新生代GC:YGC / 全局GC:FGC / )
- -gccapacity: 比-gc多了各区域的最大/最小空间
- -gccause: 比-gc多了gc原因
- -gcnew: 新生代gc状况
- -gcnewcapacity: 略
- -gcold: 老年代gc状况
- -gcoldcapacity: 略
- -compiler: JIT编译器编译方法的信息
- -printcompilation: 已被JIT编译的方法
- vmid:
- 本地: VMID 和 LVMID 一致
- 远程:
[protocol:][//]lvmid[@hostname[:port]/servername]
- interval: 查询间隔
- s|ms
- count: 查询次数
- option
- 示范:
jstat -class 12984 100 20
- 格式:
- jinfo: Java配置信息
- 格式: jinfo [option] pid
- option:
- -flag : 查询指定参数
- -flag [+|-]: 添加/删除 参数
- -flag =: 修改参数
- -flags: 显示全部参数
- -sysprops: 打印System.getProperties()的内容
<no option>
- 示范:
jinfo -flag OldSize 12984
- jmap: Java内存映射
- 格式:
jmap [option] vmid
- option:
- -dump: 生成Java堆转储快照, 格式: -dump:[live,]format=b,file=, live参数表示是否dump出存活对象
- -finalizerinfo: 在F-Queue中等待Finalizer线程执行finalize方法的对象
- -heap: Java堆详情
- -histo: Java堆中对象的统计信息
- -F: 强制生成dump快照 (Linux有效/Windows无效)
- 示范:
jmap -dump:format=b,file=javamemory.bin 12984
- 格式:
- jhat: dump快照分析工具 (不要在服务器上直接分析)
- 使用: 执行
jhat C:\Users\LZLuz\javamemory.bin
=> 看到Server is ready.
后,在浏览器中输入http://localhost:7000
=> 分析内存泄露主要看Show heap histogram
- 使用: 执行
- jstack: 线程堆栈跟踪, 生成当前线程快照 (主要用于定位线程长时间卡顿的原因)
- 格式: jstack [option] vmid
- option:
- -F: 强制输出线程堆栈
- -l: 显示所有信息(附加锁信息)
- -m: 显示Native堆栈
- 示范: jstack -l 12984
- jps: 所有HotSpot虚拟机进程的LVMID
GUI工具
- JConsole: Java监视和管理控制台
- Java自带工具, 命令行运行
jconsole
即可
- Java自带工具, 命令行运行
- VisualVM: Java监视和故障处理工具
- Java6+自带工具, 命令行运行
jvisualvm
即可, 强大而值得推荐
- Java6+自带工具, 命令行运行
- JConsole: Java监视和管理控制台
优化建议
- 尽量避免有大量的生存时间长的大对象产生, 这样才能减少full gc, 保障老年代空间的稳定.
- 扩展堆空间固定(-Xmx, -Xms)到合理的大小, 已避免扩展带来性能浪费, 编写合理的代码, 尽量没有full gc, 这样程序的响应速度才能有保障.
- 扩大(-Xmn)新生代空间到合理的大小, 已减少新生代GC
- 代码优化: 减少创建非必要的对象; 非线程安全需要, 尽量使用非线程安全的数据结构存储数据
阅读全文
0 0
- java_内存
- Java_内存
- 走进java_内存分配
- Java_内存管理和继承
- 菜鸟先飞之JAVA_内存分配
- Java_基础—内存输出流
- Java_内存泄漏_实例1
- Java_内存模型_主内存与工作内存及交互操作
- Java_内存模型_主内存与工作内存及交互操作
- Java_内存溢出(Memory Overflow)和内存泄露(Memory Leak)的区别
- Java_内存分区堆(heap) 栈(stack)和方法区(method)
- JAVA_ 环境变量
- Java_多态
- Java_总结
- java_集合
- Java_关键字
- Java_异常
- Java_泛型
- Linux系统编程——管道
- 数据结构的三个问题
- QRegExp
- Term Dictionary和Index文件 (FST详细解析)-- 基于lucene4
- win7/10中以管理员身份运行bat脚本时,获取当前文件所在目录
- Java_内存
- 【半原创】Irp占坑保护文件不被删除
- Window系统下安装tomcat图文教程
- 子框架页面跳出到父框架页面代码
- 安装NERDTree
- mysql远程连接
- 推荐几款有趣的实用app
- Python解析MNIST数据集
- 任意次方的后三位,打鱼晒网,计算某年的天数!