1、内存分布web
Java虚拟机所管理的内存将会包括如下几个运行时数据区域,以下图所示:算法
本地方法栈数组
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的做用是很是类似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并无强制规定,所以具体的虚拟机能够自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与缓存
虚拟机栈同样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。安全
(我的理解:虚拟机栈就是一般所说的栈,本地方法栈服务于native方法)数据结构
运行时常量池并发
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各类字面量和符号引用,这部份内容将在类加载后存放到方法区的运行时常量池中。框架
直接内存(Direct Memory)jvm
直接内存并非JVM管理的内存,但这部份内存常常被使用,也可能致使OutOfMemoryError异常。函数
JDK中有一种基于通道(Channel)和缓冲区(Buffer)的I/O方式,将由C语言实现的native函数库直接分配堆外内存,用存储在JVM堆中的DirectByteBuffer来引用。因为直接内存收到本机器内存的限制,因此也可能出现OutOfMemoryError的异常。
2、须要回收的区域和对象
回收区域:堆和方法区
如何判断一个对象已死(便可回收对象),通常有引用计数算法和可达性分析算法
一、引用计数算法:每一个对象都有一个引用计数器,每当有一个地方引用它时计数器就加1,引用失效时计数器就减1,当计数器为0时,该对象就能够回收。缺点是循环引用对象不能被回收。
二、可达性分析算法:一系列“GC ROOTs”的对象做为起点,当某个对象与GC ROOTS不可达时,该对象即被判断为可回收对象。能够做为GC ROOTs对象的有如下几种:
三、扩充的引用
引用分为四种:(不会被回收的,可回收也可不回收,确定会被回收、不须要理会的)
a> 强引用(Strong Reference).就是为刚被new出来的对象所加的引用,它的特色就是,永远不会被回收。
b> 软引用(Soft Reference).声明为软引用的类,是可被回收的对象,若是JVM内存并不紧张,这类对象能够不被回收,若是内存紧张,则会被回收。此处有一个问题,既然被引用为软引用的对象能够回收,为何不去回收呢?其实咱们知道,Java中是存在缓存机制的,就拿字面量缓存来讲,有些时候,缓存的对象就是当前无关紧要的,只是留在内存中若是还有须要,则不须要从新分配内存便可使用,所以,这些对象便可被引用为软引用,方便使用,提升程序性能。
c> 弱引用(Weak Reference).弱引用的对象就是必定须要进行垃圾回收的,无论内存是否紧张,当进行GC时,标记为弱引用的对象必定会被清理回收。
d> 虚引用(Phantom Reference).虚引用弱的能够忽略不计,JVM彻底不会在意虚引用,其惟一做用对象被回收时会收到一些通知。
四、对象回收处理过程
对象被回收前须要进行两次标记过程。第一次是对象与GC Roots间不可达,即被第一次标记而且筛选该对象是否有必要执行finalize()方法,将筛选的对象放到一个执行队列中,没有必要执行finalize方法的对象会被回收;第二次仅对队列中的对象进行标记,若是在执行finalize方法中将对象与GC ROOTS关联上,则该对象将不会被回收,不然该对象将被回收。
3、内存分配
Java内存分配和回收的机制归纳的说,就是:分代分配,分代回收。对象将根据存活的时间被分为:年轻代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法区)
年轻代(Young Generation):对象被建立时,内存的分配首先发生在年轻代(大对象能够直接 被建立在年老代),大部分的对象在建立后很快就再也不使用,所以很快变得不可达,因而被年轻代的GC机制清理掉(IBM的研究代表,98%的对象都是很快消 亡的),这个GC机制被称为Minor GC或叫Young GC。注意,Minor GC并不表明年轻代内存不足,它事实上只表示在Eden区上的GC。
年轻代上的内存分配是这样的,年轻代能够分为3个区域:Eden区(伊甸园,亚当和夏娃偷吃禁果生娃娃的地方,用来表示内存首次分配的区域,再 贴切不过)和两个存活区(Survivor 0 、Survivor 1)。内存分配过程为:
年老代(Old Generation):对象若是在年轻代存活了足够长的时间而没有被清理掉(即在几回 Young GC后存活了下来),则会被复制到年老代,年老代的空间通常比年轻代大,能存放更多的对象,在年老代上发生的GC次数也比年轻代少。当年老代内存不足时, 将执行Major GC,也叫 Full GC。
4、垃圾收集算法
一、标记-清除算法
分两个阶段,标记阶段和清除阶段。首选标记出全部须要回收的对象,标记完成后统一回收全部被标记的对象。
二、复制算法
将内存分为大小相等的两块,每次只使用其中一块。当一块内存用完了,就将活着的对象复制到另外一块上面,最后一次性清除已使用过的内存空间。(通常新生代采用这种算法)
三、标记-整理算法
也是分为两个阶段。第一个阶段是标记,第二个阶段是将全部存活对象移动到一端,而后直接清理掉边界之外的内存。
四、分代收集算法
根据对象存活时间不一样将内存分为几部分。通常将堆分为新生代和年老代,根据年代的特色采用最适合的收集算法。例如,新生代通常采用复制算法,年老代采用标记-清除或者标记-整理算法。
5、JVM经常使用内存参数设置
a: -Xmx<n>
指定 jvm 的最大 heap 大小 , 如 :-Xmx=2g
b: -Xms<n>
指定 jvm 的最小 heap 大小 , 如 :-Xms=2g , 高并发应用, 建议和-Xmx同样, 防止由于内存收缩/忽然增大带来的性能影响。
c: -Xmn<n>
指定 jvm 中 New Generation 的大小 , 如 :-Xmn256m。 这个参数很影响性能, 若是你的程序须要比较多的临时内存,建议设置到512M, 若是用的少, 尽可能下降这个数值, 通常来讲128/256足以使用了。
d: -XX:PermSize=<n>
指定 jvm 中 Perm Generation 的最小值 , 如 :-XX:PermSize=32m。 这个参数须要看你的实际状况,。能够经过jmap 命令看看到底须要多少。
e: -XX:MaxPermSize=<n>
指定 Perm Generation 的最大值 , 如 :-XX:MaxPermSize=64m
f: -Xss<n>
指定线程桟大小 , 如 :-Xss128k, 通常来讲,webx框架下的应用须要256K。 若是你的程序有大规模的递归行为,请考虑设置到512K/1M。 这个须要全面的测试才能知道。 不过,256K已经很大了。 这个参数对性能的影响比较大的。
g: -XX:NewRatio=<n>
指定 jvm 中 Old Generation heap size 与 New Generation 的比例 , 在使用 CMS GC 的状况下此参数失效 , 如 :-XX:NewRatio=2
h: -XX:SurvivorRatio=<n>
指 定 New Generation 中 Eden Space 与一个 Survivor Space 的 heap size 比例 ,-XX:SurvivorRatio=8, 那么在总共 New Generation 为 10m 的状况下 ,Eden Space 为 8m
i: -XX:MinHeapFreeRatio=<n>
指定 jvm heap 在使用率小于 n 的状况下 ,heap 进行收缩 ,Xmx==Xms 的状况下无效 , 如 :-XX:MinHeapFreeRatio=30
j: -XX:MaxHeapFreeRatio=<n>
指定 jvm heap 在使用率大于 n 的状况下 ,heap 进行扩张 ,Xmx==Xms 的状况下无效 , 如 :-XX:MaxHeapFreeRatio=70
k: -XX:LargePageSizeInBytes=<n>
指定 Java heap 的分页页面大小 , 如 :-XX:LargePageSizeInBytes=128m
6、内存分配与回收策略
对象优先在Eden分配;
大对象直接进入老年代;(例如:长字符串和数组)
长期存活的对象将进入老年代;(Survivor空间中通过必定次数minor GC仍旧存活的对象)
动态对象年龄断定;(Survivor空间中对象不必定非等到必定年龄才能移动到年老代,也能够经过相同年龄对象总大小大于Survivor空间的一半,该年龄及以上大小的对象都被移动到年老代)
空间分配担保;(根据年老代上的最大可用连续空间是否大于整个新生代对象的空间,若是大于能够确保是安全的,能够直接minor GC;若是不是根据状况选择minor GC仍是full GC)