根据JVM规范,最初Java内存分为5个区域,分别为堆(Heap)、JVM栈(Stack)、方法区(Method Area)、本地方法栈(Native Method Stack)、程序计数器(Program Counter Register)。方法区中,还有一块运行时常量池(Runtime Constant Pool)区域。java
在JDK1.4版本新增的NIO特性中,新增了本地内存(Native Heap),此内存区域在JVM外分配,由OS管理,此区域又叫直接内存(Direct Memory),经过ByteBuffer
的静态方法allocateDirect
可在本地内存中分配直接内存Buffer,并经过JVM堆中的DirectByteBuffer
对象引用此Buffer。服务器
方法区主要用于存储类的元数据、运行时常量等数据,在JVM规范中,对于方法区的管理比较宽松,没有明肯定义方法区的实现位置,JVM参考虚拟机HotSpot的实现上,把方法区放在堆的永久代中。多线程
随着Spring等使用动态字节码、反射、代理技术框架的流行,运行过程当中生成大量的类,方法区存储的内容愈来愈多,致使方法区内存容易溢出,出现 java.lang.OutOfMemoryError: PremGen space
异常。框架
在JDK7开始对方法区结构进行了优化拆分,并在JDK8中完全去掉了方法区。优化方法包括把类的元信息、符号引用(Symbols)移到本地内存区域,把静态变量(class static)、字面量(internal strings)移到堆区域。其中存储类的元信息区域叫作元空间(Metaspace)。优化
区域 | 特征 | 做用 | 配置参数 | 异常 | 生命周期 |
---|---|---|---|---|---|
堆 | 线程共享 | 类实例对象存储空间 | -Xms -Xsx -Xmn | OutOfMemoryError | JVM |
栈 | 线程私有 | 线程栈帧,局部变量、方法参数、操做数、动态连接等信息 | -Xss(帧数) | StackOverflowError OutOfMemoryError | 线程 |
本地方法栈 | 线程私有 | 线程调用Native方法栈帧 | - | OutOfMemoryError | 线程 |
程序计数器 | 此区域很小,为线程私有 | 记录线程运行的字节码行号等线程运行位置信息,用于线程的切换和恢复 | - | - | 线程 |
直接内存 | 在OS Native内存中分配,可突破JVM内存大小限制 | 经过相似mmap在OS内存中分配,可避免内存在OS和JVM间拷贝 | - | OutOfMemoryError | JVM |
元空间 | 在OS Native内存中分配 | 保存类的元信息、符号引用等信息 | XX:MetaspaceSize XX:MaxMetaspaceSize | - | JVM |
运行时常量池 | 在堆中分配 | 保存静态变量、字面量等数据 | - | - | JVM |
每一个线程运行时,会在栈中分配一个线程栈,栈由栈帧(Stack Frame)组成,每次调用方法时会生成一个栈帧,方法返回时则弹出栈顶帧。栈中保存了方法入参、局部变量、操做数栈、动态连接、方法出口信息等数据。当一个线程栈帧的栈数量超过-Xss定义的帧数时,会抛出StackOverflowError异常,通常在递归调用时容易发生此错误。ui
JDK8中,堆中移除了永生代区域,堆内存主要由新生代和老年代两部分组成。其中新生代由一个伊甸园(Eden)和两个幸存者(Survivor)3部分组成,新生代的垃圾回收频率高,Minor GC时,把Eden和其中一个Survivor中的存活对象拷贝到另外一个Survivor区,并清除前面两个区域的数据,经过这种结构和回收方式来提升垃圾回收效率,减小内存碎片。通过若干(默认15)次后还存活的对象,将进入老年代区,当老年代数据慢是会触发Major GC。spa
老年代:新生代的内存大小默认比例为2:1。Eden和两个Survivor的比例为8:1:1。内存的分配比例能够经过 java -XX:+PrintFlagsFinal -version
命令进行查看。操作系统
[Global flags]
uintx InitialSurvivorRatio = 8
uintx NewRatio = 2
设置内存区域比例参数:线程
参数 | 说明 |
---|---|
-XX:InitialSurvivorRatio | 新生代Eden/Survivor空间的初始比例 |
-XX:Newratio | 老年代和新生代的内存比例 |
当前线程所执行的行号指示器。经过改变计数器的值来肯定下一条指令,好比循环,分支,跳转,异常处理,线程恢复等都是依赖计数器来完成。设计
Java虚拟机多线程是经过线程轮流切换并分配处理器执行时间的方式实现的。为了线程切换能恢复到正确的位置,每条线程都须要一个独立的程序计数器,因此它是线程私有的。
java.lang.OutOfMemoryError:GC overhead limit exceeded
这是JDK6新增错误类型,当GC为释放很小空间占用大量时间时抛出;通常是由于堆过小,致使异常的缘由,没有足够的内存。解决方案:
java.lang.OutOfMemoryError: unable to create new native thread
可能缘由是系统内存耗尽,没法为新线程分配内存或者建立线程数超过了操做系统的限制。经过两个途径解决: