jvm---1、内存区域划分及配置

JVM的内存区域划分

jvm---1、内存区域划分及配置_第1张图片

jvm 内存可分为 
方法区、堆   ----------------------  线程共享的,也就是说在这里面的变量线程不安全。
虚拟机栈、本地方法栈、程序计数器 --- 线程独享
直接内存 ----- 图中没有画出,记得UnSafe 有方法好像用的是直接内存, 如果 DirectBufferCache 就是使用的直接内存做缓存的

其中堆又分为年轻代,老年代。年经代又分为一个Eden区和两个Survivor 区(两个Survivoer 一个小from  一个叫to ),两者比例默认是8:1(后面再详解,这个和内存分配、GC有关系 )。

作用
1、方法区    
用于存储已被加虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码数据。提一下:有叫“非堆”的,HotSpot 上叫“永久代”,使用了永久代来实现方法区而已。永久代改为Native Memory 实现了,jdk1.7中已经把永久代的字体串常量移出。
该区域内存回收,主要是常量池回收和对象类型卸载,这个区域的内存回收率比较小,尤其是类型卸载条件比较苛刻。
无法满足内存分配的话会抛出 OutOfMemoryError 异常

2、堆

内存中最大的一块,唯一的目的就是存放对象实例,几乎所有的实例对象在这时分配内存(JVM 规范:所有对象及数组都要在堆上分配)。JIT 编译器及逃逸分析技术成熟分导致一些微妙的变化。

        堆分成新生代和老年代,新生代又分为一个Eden区和两个Survivor 区(分别叫 from、to)。在后面讲GC 时还会提到

无法满足内存分配的话会抛出 OutOfMemoryError 异常

3、虚拟机栈
线程私有的,生命周期和线程相同。虚拟机本描述的是Java 方法执行的内存模型:每个方法在执行的同时会创建一个栈贞(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用到执行完成过程,就是栈贞在虚拟机栈中入栈到出栈的过程。
局部变量表所用内存在编译期间已经确定了(基本数据类型和引用占用一个字节,double long 占用两个字节),不会改变。
如果线程请求的栈深度大于虚拟机所允许的深度,会抛出StackOverflowError。虚拟机栈是可以动态扩展的,不过是允许固定长度的虚拟机栈。如果在扩展过程中无法申请到足够内存会抛出OutOfMemoryError异常

4、本地方法栈   Native Method Stack 
与虚拟机栈作用相似,一个是为虚拟机执行java 方法服务的,一个是虚拟机调用Native 方法的服务的。
也会抛出:StackOverflowError 和 OutOfMemoryError

5、程序计数器
一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。jvm 多线程就是通过线程轮流切换并分配处理器时间的方式来实现的,在任何一个时间处理器只能处理一条线程中的指令。所以为了线程切换后能恢复到正确的指令位置,每一个线程都有一个独立的程序计数器。
如果是在执行一个Native 方法,这个计数器是空(Undefined), jvm 规范中唯一一个没有OutOfMemoryError 的区域

6、直接内存  Direct Memory
NIO 可以使用Native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作(有一些堆缓存的实现也是这种方式实现的),这样就避免了java 堆和Native 堆中来回复制数据,所以NIO效率就高。

直接内存不受java堆大小限制,但是受物理内存和处理器的寻址空间限制,所有会抛出OutOfMemoryError

 

堆大小设置


配置示例:

java -Xmx4g -Xms4g -Xmn3g  -XX:PermSize=128m -XX:MaxPermSize=128m  -Xss256k

-Xmx 设置堆的最大空间大小
-Xms 设置堆初始值,server端最好Xms与Xmx一样
-Xmn 设置新生代空间大小 1.4版本以后,是eden+ 2个survivor 空间大小
-XX:NewSize 新生代大小
-XX:MaxNewSize 新生代最大大小
-XX:NewRatio 新生代与老年代的比例
-XX:PermSize 设置永久代最小空间大小   持久代一般固定大小为64m
-XX:MaxPermSize 设置永久代最大上线
-Xss 设置每个线程的堆栈大小,1.5后默认是1m
-XX:MaxDirectMemorySize     设置最大堆外内存大小
去掉 Survivor 空间,让Eden区对象经过一次 Minor GC 后直接进入老年代 -XX:SurvivorRatis=65535 -XX:MaxTenuringThreshold=0 或者:-XX:AlwaysTenure(堆外内存  只有等待Full GC 后,顺便地帮它清理内存的死亡对象)

整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小   (方法区什么的没有计算在内)


测试:
>java -Xmx4g -Xms4g -Xmn3g  -XX:PermSize=128m -XX:MaxPermSize=128m  -Xss256k
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0
.... 省略后面的错误,没有指定执行的class

HotSpot 虚拟机忽略了 PermSize 设置


还有两个比值:可以通过这两个比值来分配内存比例
-XX:NewRatio=4       设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。年轻代:年老代=4(老年代就新生带的4倍),年轻代占整个堆栈的1/5。  默认是2
-XX:SurvivorRatio=8  设置年轻代中Eden区与Survivor区的大小比值。Eden:Survivor:=8:1 , 所以 Eden 占 8/10, Survivor 占 2/10

查看 SurvivorRatio 设置
$ java -XX:+PrintFlagsInitial | grep SurvivorRatio
        uintx InitialSurvivorRatio                      = 8                                   {product}
        uintx MinSurvivorRatio                          = 3                                   {product}
        uintx SurvivorRatio                             = 8                                   {product}
        uintx TargetSurvivorRatio                       = 50                                  {product}

查看 NewRatio 设置
$ java -XX:+PrintFlagsInitial | grep  NewRatio
        uintx NewRatio                                  = 2                                   {product}

 

参考:《深入理解jvm虚拟机》

jvm---1、内存区域划分及配置_第2张图片

你可能感兴趣的:(jvm)