不同 counter undefine 并不是 ans color bold ping catch
1. 程序计数器(Program Counter Register)
当前线程所执行的字节码的行号指示器.如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 native 方法,这个计数器值则为空(Undefined).
线程私有.唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域.
2. Java 虚拟机栈(Java Virtual Machine Stack)
虚拟机栈描述的是 Java 方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息.每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈的过程.
线程私有.大小通过 - Xss 设置,例如:-Xss1m,HotSpot 默认:-Xss:1m
此参数的大小和能创建的最大线程数量有着密切的关系,在堆内存固定的情况下此参数值越大能够创建的线程越小.在此参数固定的情况下堆内存越大能够创建的线程越少,如下是 32 位 HotSpot 虚拟机 1.7.0_80 版本下不同 - Xss 值能够创建的最大线程的数量:
堆内存 | 虚拟机栈 | 最大线程数量 |
-Xmx1g | -Xss128k | 2000 |
-Xmx1g | -Xss256k | 1512 |
-Xmx1g | -Xss512k | 1017 |
-Xmx1g | -Xss1m | 620 |
之所以会出现这种情况是因为操作系统分配给每个进程的内存是有限制的,譬如 32 位的 Windows 限制为 2GB.虚拟机提供了参数来控制 Java 堆和方法区的这两部分内存的最大值.剩余的内存为 2GB(操作系统限制)减去 Xmx(最大堆内存),再减去 MaxPermSize(最大方法区容量),程序计数器消耗内存很少,可以忽略不计.如果虚拟机进程本身消耗的内存不计算在内,剩下的内存就由虚拟机栈和本地方法栈瓜分了.每个线程分配到的栈容量越大,可以建立的线程数量自然就越少,建立线程时就越容易把剩下的内存耗尽.当不能创建更多线程的时候会 java.lang.OutOfMemoryError: unable to create new native thread 异常,例如下面的程序:
package com.zws.jvm;
import java.util.concurrent.atomic.AtomicInteger;
/**
* JVM args: -server -Xmx1g -Xss512k
* @author wensh.zhu
*
*/
public class XssMaxThread {
public static final AtomicInteger counter = new AtomicInteger();
public static void main(String[] args) {
try {
while (true) {
new Thread(new Runnable() {
public void run() {
try {
counter.incrementAndGet();
Thread.sleep(1000 * 60 * 60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
} catch (Throwable e) {
System.out.println(counter.get());
e.printStackTrace();
}
}
}
输出如下:
980
java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:714)
at com.zws.jvm.XssMaxThread.main(XssMaxThread.java:24)
综上此异常可通过适当减小堆内存或虚拟机栈内存解决.
Java 虚拟机规范中有两种异常:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常;如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出 OutOfMemoryError 异常.如下代码:
package com.zws.jvm;
/**
* JVM args:-server -Xss128k
* @author wensh.zhu
*
*/
public class StackOver {
int num = 0;
public static void main(String[] args) {
StackOver stackOver = new StackOver();
try {
stackOver.count();
} catch (Throwable e) {
System.out.println("stack length:" + stackOver.num);
e.printStackTrace();
}
}
public void count() {
num ++;
count();
}
}
输出如下:
stack length:2105
java.lang.StackOverflowError
at com.zws.jvm.StackOver.count(StackOver.java:17)
3. 本地方法栈(Native Method Stack)
类似于 Java 虚拟机栈,服务于 native 方法.HotSpot 虚拟机直接把本地方法栈和虚拟机栈合二为一;此内存空间会抛出 StackOverflowError 和 OutOfMemoryError 异常.
4. Java 堆(Java Heap)
用于存放对象实例和数组,java 虚拟机规范规定:所有对象实例以及数组都要在堆上分配.可细分为:新生代和老年代.新生代又分为:Eden 空间,From Survivor 空间,To Survivor 空间.之所以这么分是为了优化 GC 性能.
大小通过 - Xms 和 - Xmx 设置大小,-Xms: 初始大小,-Xmx: 最大大小.例如:-Xms1024m -Xmx1024m
还可以通过以下参数设置各个空的大小以及比例:
-XX:NewRatio 老年代大小与新生代大小的比例,默认-XX:NewRatio=2
-XX:NewSize 新生代初始大小,默认-XX:NewSize=2m
-XX:MaxNewSize 最大新生代大小,默认值通过-XX:NewRatio计算得来
-XX:SurvivorRatio Eden和其中一个Survivor的比值 默认-XX:SurvivorRatio=8,即Edem:from:to = 8:1:1
线程共享.可抛出 OutOfMemoryError 异常.如下代码:
package com.zws.jvm;
import java.util.LinkedList;
import java.util.List;
/**
* JVM args:-server -Xmx128m
* @author wensh.zhu
*
*/
public class OOMTest {
private int[] numbers = new int[1024];
public static void main(String[] args) {
List < OOMTest > testObjs = new LinkedList < >();
for (;;) {
testObjs.add(new OOMTest());
}
}
public int[] getNumbers() {
return numbers;
}
public void setNumbers(int[] numbers) {
this.numbers = numbers;
}
}
输出如下:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.zws.jvm.OOMTest.<init>(OOMTest.java:11)
at com.zws.jvm.OOMTest.main(OOMTest.java:16)
5. 方法区(Method Area)
用于存储已被虚拟机加载的类信息,常量,静态变量,及时编译器编译后的代码等数据.
HotSpot 虚拟机习惯上称之为永久代,可通过 - XX:PermSize 和 - XX:MaxPermSize 参数设置初始值以及最大值,例如:-XX:PermSize=128m,-XX:MaxPermSize=128m,在 Java8 中这两个参数以及被移除,取而代之的是 - XX:MetaspaceSize 和 - XX:MaxMetaspaceSize
线程共享.可抛出 OutOfMemoryError 异常.
6. 运行时常量池(Runtime Constant Pool)
方法区的一部分.用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放.
7. 直接内存
直接内存(DirectMemory) 并不是虚拟机运行吋数据 K 的一部分,在 JDK1.4 中新加入了 NIO(New Iput/Output) 类,引入一种基于通道(Channel) 与缓冲区(Buffer) 的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBufTer 对象作为这块内存的引用进行操作.这样能在一些场景中显著提髙性能,闪为避免了在 Java 堆和 Native 堆中来回复制数据.显然,本机直接内存的分配不会受到 Java 堆大小的限制,但是,既然是内存,肯定还是会受到本机总内存(包括 RAM 以及 SWAP 区或者分页文件)大小以及处理器寻址空间的限制.服务器管理员在配置虚拟机参数时,会根据实际内存设置 - Xmx 等参数信息,但经常忽略直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),从而导致动态扩展时出现 OutOfMemoryError 异常.
DirectMemory 容量可通过 - XX:MaxDirectMemorySize 指定,如果不指定,则默认与 Java 堆最大值(-Xmx 指定)一样.
参考周志明《深入理解 Java 虚拟机 JVM 高级特性与最佳实践》
Java 内存模型学习总结
来源: http://www.bubuko.com/infodetail-2469047.html