概述

一 . 内存空间
Sun JDK在实现时遵照JVM规范,将内存空间划分为
PC寄存器、
JVM方法栈、
方法区、
堆、
本地方法栈。如下图:


1. 方法区
• 存放了要加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息。
如通过Class对象的getName、isInterface等方法来获取的信息都来源于方法区。
• 方法区域是全局共享的,在一定条件下会被GC。
• 在Sun JDK中这块区域对应
Permanet Generation,又称为
持久代,默认最小值为16MB,最大值为64MB,可通过-XX:Permsize及-XX:MaxPermsize来指定最小值和最大值。
2. 堆(Heap)
• 用于存储对象实例及数组值,即所有通过new创建的对象的内存都在此分配,Heap中的内存由GC回收。
• 堆的大小可以通过
-Xms和
-Xmx来控制。-Xms为JVM启动时申请的最小Heap内存,默认为物理内存的1/64但小于1GB;-Xmx为JVM启动时申请的最大Heap内存,默认为物理内存的1/4但小于1GB。
• 默认当
空余堆内存小于40%时,JVM会增大Heap到-Xmx指定的大小,可通过-XX:MinHeapFreeRatio=来指定这个比例;当
空余堆内存大于70%时,JVM会减小Heap的大小到-Xms指定的大小可通过-XX:MaxHeapFreeRatio=来指定这个比例。
• 对于运行系统而言,为避免在运行时频繁调整Heap的大小,通常将
-Xms和
-Xmx的值设成一样。
3. Sun JDK从1.2开始对堆采用了分代管理的方式
• 新生代:Java程序中
新建的对象都从新生代分配内存。
• 旧生代:用于存放
新生代中经过多次垃圾回收仍然存活的对象,例如缓存对象。
分代管理如下图所示:

4. 本地方法栈
• 用于支持native方法(方法中调用了不是由java语言编写的代码)的执行。
• 在Sun JDk中,本地方法栈和JVM方法栈是同一个。
5. PC寄存器和JVM方法栈
• 每个线程都会创建PC寄存器和JVM方法栈。
• JVM方法栈为线程私有,其在内存分配上非常高效。当方法运行完毕时,其对应的栈帧所占用的内存会自动释放。
• 在Sun JDK中,通过-Xss来指定JVM方法栈的大小。
6. Java堆内存(heap memory)的十个要点
①. Java堆内存是操作系统分配给JVM的内存的一部分。
②. 当我们创建对象时,它们存储在Java堆内存中。
③. 为了便于垃圾回收,Java堆空间分成三个区域,分别叫作New Generation, Old Generation或叫作Tenured Generation,还有Perm Space。
④. 你可以通过用JVM的命令行选项 -Xms, -Xmx, -Xmn来调整Java堆空间的大小。不要忘了在大小后面加上”M”或者”G”来表示单位。举个例子,你可以用 -Xmx256m来设置堆内存最大的大小为256MB。
⑤. 你可以用JConsole或者 Runtime.maxMemory(), Runtime.totalMemory(), Runtime.freeMemory()来查看Java中堆内存的大小。
⑥. 你可以使用命令“jmap”来获得heap dump,用“jhat”来分析heap dump。
⑦. Java堆空间不同于栈空间,栈空间是用来储存调用栈和局部变量的。
⑧. Java垃圾回收器是用来将死掉的对象(不再使用的对象)所占用的内存回收回来,再释放到Java堆空间中。
⑨. 当你遇到java.lang.outOfMemoryError时,不要紧张,有时候仅仅增加堆空间就可以了,但如果经常出现的话,就要看看Java程序中是不是存在内存泄露了。
⑩. 请使用Profiler和Heap dump分析工具来查看Java堆空间,可以查看给每个对象分配了多少内存。
二 . 内存分配
1. Java对象内存分配
•
Java对象所占用的内存主要从
堆上进行分配。
•
堆是所有线程共享的。所以,
在堆上分配内存时需要进行
加锁,这导致创建对象的开销比较大。
• 当堆上内存不足时,会触发GC,如果GC后空间仍然不足,则抛出OutOfMemory错误信息。
2. 提升分配效率(Sun JDK)-TLAB
• Sun JDK会为每个新创建的线程在新生代的
Eden Space上分配一块独立的空间,这块空间称为TLAB(Thread Local Allocation Buffer)。
• TLAB的大小由JVM计算得出,可通过-XX:TLABWasteTargetPercent来设置TLAB占用Eden Space的百分比,默认值为1%。
•
在TLAB上分配内存时
不需要加锁,因此JVM在给线程中的对象分配内存时会尽量在TLAB上分配。
• 如果
对象过大或TLAB空间已用完,则仍然在堆上分配。因此在编写Java程序时,通常
多个小的对象比大的对象分配起来更加高效。
• 除了TLAB,还有一种基于逃逸分析直接在栈上进行分配的方式。
3. 分配方式
①
堆上分配
②
栈上分配
③
堆外分配
三 . 内存回收
1. 对象存活的判定
• 引用计数
•
可达性分析

2. JVM GC
•
引用计数收集器
•
跟踪收集器
过程
:注意,红叉为不存活的对象所占用内存空间

过
程
:

优缺
点
:在空间中存活对象较多的情况下较为高效,但由于该算法为直接回收不存活对象所占用的内存,因此会造成内存碎片。
过程:
优缺点:在“标记-清除”的基础上还需要进行对象移动,成本相对较高,好处则是不产生内存碎片。