JVM--内存分布

java内存划分

共享内存区域

方法区

线程隔离内存区域

程序计数器
本地方法栈
虚拟机栈

程序计数器

每个线程都有独立的程序计数器,用于取指,分支,跳转,循环,恢复线程等操作

PC=PC+1(指令字节长)

如果线程执行的是一个java方法,则这个计数器记录的是虚拟机字节码指令;如果线程执行的是一个native方法,则这个计数器值为空,此内存区域是唯一一个在java虚拟机规范中没有规定任何OOMError的区域

native方法:即不是由Java实现,而是由其他语言实现的方法

Java不是完美的,Java的不足除了体现在运行速度上要比传统的C++慢许多之外,Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。  
可以将native方法比作Java程序同C程序的接口,其实现步骤:
1、在Java中声明native()方法,然后编译。   
2、用javah产生一个.h文件。   
3、写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件)。   
4、将第三步的.cpp文件编译成动态链接库文件。  
5、在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。   
JAVA的native方法适用的情况:   
1、为了使用底层的主机平台的某个特性,而这个特性不能通过JAVA API访问。
2、为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用JAVA编写的。  
3、为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。

虚拟机栈

生命周期与java线程相同,存放 局部变量表,操作数栈,动态链接,方法出口等信息
局部变量表所需内存空间在编译时确定,而不会在运行时修改,所以,此区域会有两种异常

  1. StackOverFlowError
    线程请求栈的深度超过虚拟机所允许的深度
  2. OutOfMemoryError
    栈动态扩展到无法申请到足够的内存

扩展:

1
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k

-Xmx3550m:设置JVM最大可用内存为3550M。
-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

1
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。

本地方法栈

服务于native方法

Java堆

java虚拟机中最大的一块内存部分,用于存放对象实例

方法区

存放虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等
运行时常量池:存放编译器生成的各种字面量与符号引用

1
javap -v Person

这里讲的java常量池很详细
符号引用:

1
2
String s = "aaa";
System.println.out("sout:"+s);

直接引用:

1
System.println.out("sout:"+"aaa");

运行时也可以将新的常量放到常量池

直接内存

堆外内存,NIO支持直接分配对外内存