虚拟机应该给虚拟机分配多少内存存

虚拟机启动相关的一些内存设置参数
用圣才电子书APP或微信扫一扫,在手机上阅读本文,也可分享给你的朋友。
在Java虚拟机规范的描述中,除了PC(程序计数器)寄存器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError异常的可能。当发生OutOfMemoryError异常时,无法用try...catch捕捉。
在开始讲解之前,在这里先简单介绍下虚拟机启动相关的一些内存设置参数。因为OutOfMemoryError异常发生,与这些参数的设置密切相关。
举例说明含义:
每个线程的java栈大小,一个线程java栈所有栈帧大小总和最大允许的尺寸128k。
表示JVM Heap(堆内存)最小尺寸128MB,初始分配
表示JVM Heap(堆内存)最大允许的尺寸256MB,按需分配。
-XX:PermSize=20M
设置方法区的初始大小
-XX:MaxPermSize=30M
设置方法区的最大值
Java栈溢出
在Java虚拟机规范中,对这个区域规定了两种异常状况:StackOverflowError和OutOfMemoryError异常。
1.StackOverflowError异常
每当java程序代码启动一个新线程时,Java虚拟机都会为它分配一个Java栈。Java栈以帧为单位保存线程的运行状态。当线程调用java方法时,虚拟机压入一个新的栈帧到该线程的java栈中。只要这个方法还没有返回,它就一直存在。如果线程的方法嵌套调用层次太多(如递归调用),随着java栈中帧的逐渐增多,最终会由于该线程java栈中所有栈帧大小总和大于-Xss设置的值,而产生StackOverflowError内存溢出异常。例子如下:
2. * VM Args: -Xss128k
4. public class Test {
6. private int count = 0;
8. public static void main(String[] args) {
9. new Test().method();
12. public void method() {
13. System.out.println(++count);
14. method();
-Xss为128k。其中的一次测试结果为,当count的值累加到2312时,发生如下异常:
1. Exception in thread &main& java.lang.StackOverflowError
2. at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:58)
3. at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:392)
4. at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:447)
5. at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:544)
6. at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:252)
7. at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:106)
8. at java.io.OutputStreamWriter.write(OutputStreamWriter.java:190)
9. at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:111)
10. at java.io.PrintStream.write(PrintStream.java:476)
11. at java.io.PrintStream.print(PrintStream.java:547)
12. at java.io.PrintStream.println(PrintStream.java:686)
13. at jvm.Test.method(Test.java:17)
修改-Xss为1280k。其中的一次测试结果为,当count的值累加到26888时,发生StackOverflowError异常。随着-Xss参数值的增大,可以嵌套的方法调用层次也相应增加。
综上所述,StackOverflowError异常是由于方法调用的层次太深,最终导致为某个线程分配的所有栈帧大小总和大于-Xss设置的值,从而发生StackOverflowError异常。
2.OutOfMemoryError异常
java程序代码启动一个新线程时,没有足够的内存空间为该线程分配java栈(一个线程java栈的大小由-Xss参数确定),jvm则抛出OutOfMemoryError异常。例子如下:
2. * VM Args: -Xss128k
4. public class Test {
6. public static void main(String[] args) {
7. int count = 0;
8. while (true) {
9. Thread thread = new Thread(new Runnable() {
10. public void run() {
11. while (true) {
13. Thread.sleep(5000);
14. } catch (Exception e) {}
18. thread.start();
19. System.out.println(++count);
-Xss为128k。其中的一次测试结果为,当count的值累加到11887时,发生如下异常:
1. Exception in thread &main& java.lang.OutOfMemoryError: unable to create new native thread
2. at java.lang.Thread.start0(Native Method)
3. at java.lang.Thread.start(Thread.java:640)
4. at jvm.Test.main(Test.java:20)
修改-Xss为1280k。其中的一次测试结果为,当count的值累加到1270时,发生OutOfMemoryError异常。随着-Xss参数值的增大,java程序可以创建的总线程数越少。
Java堆溢出
Java堆用于储存对象实例。当需要为对象实例分配内存,而堆的内存占用又已经达到-Xmx设置的最大值。将会抛出OutOfMemoryError异常。例子如下:
2. * VM Args: -Xmx5m
4. public class Test {
6. public static void main(String[] args) {
7. int count = 0;
8. List&Object& list = new ArrayList&Object&();
9. while (true) {
10. list.add(new Object());
11. System.out.println(++count);
-Xmx为5m。其中的一次测试结果为,当count的值累加到297868时,发生如下异常:
1. Exception in thread &main& java.lang.OutOfMemoryError: Java heap space
2. at java.util.Arrays.copyOf(Arrays.java:2760)
3. at java.util.Arrays.copyOf(Arrays.java:2734)
4. at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
5. at java.util.ArrayList.add(ArrayList.java:351)
6. at jvm.Test.main(Test.java:15)
修改-Xmx为10m。其中的一次测试结果为,当count的值累加到670205时,发生OutOfMemoryError异常。随着-Xmx参数值的增大,java堆中可以存储的对象也越多。
方法区溢出
方法区用于存放java类型的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。在类装载器加载class文件到内存的过程中,虚拟机会提取其中的类型信息,并将这些信息存储到方法区。当需要存储类信息而方法区的内存占用又已经达到-XX:MaxPermSize设置的最大值。将会抛出OutOfMemoryError异常。对于这种情况的测试,基本的思路是运行时产生大量的类去填满方法区,直到溢出。这里需要借助CGLib直接操作字节码运行时,生成了大量的动态类。例子如下:
2. * VM Args: -XX:MaxPermSize=50M
4. public class Test {
6. public static void main(String[] args) {
7. int count = 0;
8. while (true) {
9. Enhancer enhancer = new Enhancer();
10. enhancer.setSuperclass(Test.class);
11. enhancer.setUseCache(false);
12. enhancer.setCallback(new MethodInterceptor() {
13. public Object intercept(Object obj, Method method, Object[] args,
14. MethodProxy proxy) throws Throwable {
15. return proxy.invoke(obj, args);
18. enhancer.create();
19. System.out.println(++count);
-XX:MaxPermSize为50m。其中的一次测试结果为,当count的值累加到3953时,发生如下异常:
1. Caused by: java.lang.OutOfMemoryError: PermGen space
2. at java.lang.ClassLoader.defineClass1(Native Method)
3. at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
4. at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
5. ... 8 more
修改-XX:MaxPermSize为100m。其中的一次测试结果为,当count的值累加到8022时,发生OutOfMemoryError异常。随着-XX:MaxPermSize参数值的增大,java方法区中可以存储的类型数据也越多。
相关的参考资料:
1.深入Java虚拟机(原书第2版)
2.深入理解Java虚拟机:JVM高级特性与最佳实践
3.互联网相关的文章
来源:网络
小编工资已与此赏挂钩!一赏一分钱!求打赏↓ ↓ ↓
如果你喜欢本文章,请赐赏:
已赐赏的人
我的电子书启动VMware虚拟机显示:物理内存不足无法使用的五种解决方法
作者:佚名
字体:[ ] 来源:互联网 时间:12-30 09:52:32
虚拟机无法启用怎么办啊?打开虚拟机时提示报错:“物理内存不足,无法使用配置的设置开启虚拟机。下面分享解决VMware虚拟机物理内存不足无法使用的五种解决方法,需要的朋友可以参考下
打开虚拟机时提示报错:&物理内存不足,无法使用配置的设置开启虚拟机。&&&解决方法如下,以下针对 Windows 8.1 的主机环境,Win7 也可能适用。
1、打开虚拟机时提示报错:&物理内存不足,无法使用配置的设置开启虚拟机。要修复此问题,请将该虚拟机的内存大小减少到 *** MB、将所有虚拟机使用的物理内存量增加到 *** MB,或调整其他内存设置,以允许交换更多的虚拟机内存。
本机应用程序和/或服务可能已锁定内存,这将阻止虚拟机启动。关闭不需要的应用程序或服务可能可以释放足够内存来启动此虚拟机。如果您以前能够在此主机上开启该虚拟机,请尝试重新引导此主机。重新引导主机后,可用于运行虚拟机的主机内存资源也许会比现在稍微多一些。&
解决方法如下,以下针对 Windows 8.1 的主机环境,Win7 也可能适用:
2、① 如果主机的操作系统版本是 Windows 8.1 或以上,请把 VMware Workstation 升级至10.0.4版本,此版本解决了在 Win8.1 下物理内存识别出错的问题。如果在升级版本之后虚拟机仍然不能正常启动,则可参考以下所有方法,依次尝试之。
1、② 重启电脑,并在电脑启动之后优先打开VMware &Workstation以让虚拟机享用足够的物理内存;
1、③ 新建一个虚拟机(任意操作系统)并启动,此时通常情况下这个新的虚拟机是可以正常运行的,之前所创建的虚拟机也可以正常运行。
1、虚拟机的菜单&编辑&&&首选项&,在左边的选项中选择&内存&。
2、将系统为虚拟机预留的主机物理内存提升至一个&警告临界值&,也就是大于这个值的时候会提示警告:预留内存量大于针对此系统建议的最大值,可能会降低系统性能。
1、修改虚拟机的配置参数文件:
C:\Documents and Settings\All Users\Application Data\VMware\VMware Workstation 或 C:\ProgramData\VMware\VMware Workstation 路径下的 config.ini,把 &prefvmx.useRecommendedLockedMemSize = &设为&TRUE& &prefvmx.minVmMemPct = &改成一个足够小的数,例如&1&。
2、然后再将系统的虚拟内存提高。右键在&计算机&上点击&属性&,在左侧点击&高级系统设置&。选择&高级&选项卡&性能&设置&按钮,选择&高级&选项卡&虚拟内存的&更改...&按钮。
3、在此处可自行设置分配到每个硬盘分区的虚拟内存分页文件大小,&无分页文件&意味着在该分区中不分配虚拟内存分页文件,页面文件占用的大小可以自定义。
注意事项:操作系统为Windows8.1或以上适用此方法,其他系统待验证。
大家感兴趣的内容
12345678910
最近更新的内容java虚拟机的内存分配及管理 - 不赢 - 推酷
java虚拟机的内存分配及管理 - 不赢
可能产生的异常类型
程序计数器
字节码执行的行序号
虚拟机根据程序计数器来选取下一条需要执行的字节码指令。若执行本地方法,则值为空
唯一一个没有规定异常产生的区域
&&&&&& 方法区
虚拟机已加载的类信息、常量、静态变量、即时编译器编译过的代码
区别于java堆,有一个别名叫Non-Heap(非堆)
方法区无法满足内存分配需求时抛出
OutOfMemoryError
Java方法运行时的局部变量表、操作栈、动态链接、方法出口等
Java方法执行的内存模型:每个方法执行的时候都会创建一个栈帧,存储局部变量表、操作栈、动态链接、方法出口等,方法执行过程就是对应的栈帧入栈到出栈。
栈溢出StackOverflowError
OutOfMemoryError
本地方法栈
本地方法的局部变量表、操作栈、动态链接、方法出口等
与java栈作用相似,区别是执行的方法是本地方法(Native Method)而不是java方法
栈溢出StackOverflowError
OutOfMemoryError
对象实例(几乎完全是)
线程共享,虚拟机启动时创建
逻辑连续,物理可不连续
OutOfMemoryError
运行时常量池
Class文件中常量池部分的编译期生成的各种字面量和符号引用,翻译出来的直接引用,类加载后放在运行时常量池中
方法区的一部分
也可以存储运行期间产生的新的常量,典型应用String类的intern()方法
OutOfMemoryError
常规内存,java虚拟机外的一块频繁使用的内存区域,使用Native方法进行内存分配,然后通过一个存储在java堆里的对象来引用这块内存
OutOfMemoryError
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致为什么虚拟机占用的实际内存超出了 -Xmx 设置的最大值 - 讨论 - 高级语言虚拟机 - ITeye群组
JBoss服务器,启动参数中设置了最大堆内存为3072M,运行一段时间后在系统任务管理器看到相应的java.exe进程占用了5G内存
因为-Xmx只用于指定Java heap的大小。JVM还有许多其它东西需要占用内存,同一进程内别的native库也可能占用内存。
您看的“占用内存量”是进程的private working set,这包括该进程所有独占(不与其它进程共享)的内存,不只是Java heap。
楼主可以参考一下JavaOne 2010的一个IBM的演讲:
虽然这个演讲的例子用的是IBM JVM,但其原理也适用与Oracle JDK / OpenJDK的HotSpot VM。
RednaxelaFX 写道因为-Xmx只用于指定Java heap的大小。JVM还有许多其它东西需要占用内存,同一进程内别的native库也可能占用内存。
您看的“占用内存量”是进程的private working set,这包括该进程所有独占(不与其它进程共享)的内存,不只是Java heap。
楼主可以参考一下JavaOne 2010的一个IBM的演讲:
虽然这个演讲的例子用的是IBM JVM,但其原理也适用与Oracle JDK / OpenJDK的HotSpot VM。
感谢RednaxelaFX的解答,你的意思我基本理解了,只是除去heap之还占了2G多的内存,这个开销可不少,有什么工具可以查看这些内存具体用在哪些地方了吗?
louhaiyun 写道除去heap之还占了2G多的内存,这个开销可不少,有什么工具可以查看这些内存具体用在哪些地方了吗?如果是linux平台的话,可以用pmap -x pid看一下或者直接看/proc/pid/smaps文件更详细windows下可以试试Procmon?看到有人说这个是windows上的pmap,但是我没用过基本上就是线程栈空间,jar文件的映射,动态库的代码段,数据段以及应用打开的文件的缓存这几类吧对象的内存分配,往大的方向上讲,就是在堆上分配,少数情况下也可能会直接分配在老年代中,分配的规则并不是百分之百固定的,其细节决定于当前使用的是哪种垃圾收集器组合,当然还有虚拟机中与内存相关的参数。垃圾收集器组合一般就是Serial+Serial Old和Parallel+Serial Old,前者是Client模式下的默认垃圾收集器组合,后者是Server模式下的默认垃圾收集器组合,文章使用对比学习法对比Client模式下和Server模式下同一条对象分配原则有什么区别。
首先讲讲什么是TLAB。内存分配的动作,可以按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。哪个线程需要分配内存,就在哪个线程的TLAB上分配。虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。这么做的目的之一,也是为了并发创建一个对象时,保证创建对象的线程安全性。TLAB比较小,直接在TLAB上分配内存的方式称为快速分配方式,而TLAB大小不够,导致内存被分配在Eden区的内存分配方式称为慢速分配方式。
对象优先分配在Eden区上
上面讲了不同的垃圾收集器组合对于内存分配规则是有影响的,看下影响在什么地方并解释一下原因,虚拟机参数为&-verbose:gc -XX:+PrintGCDetails -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8&,即10M新生代,10M老年代,10M新生代中8M的Eden区,两个Survivor区各1M。代码都是同一段
public class EdenAllocationTest
private static final int _1MB = 1024 * 1024;
public static void main(String[] args)
byte[] allocation1 = new byte[2 * _1MB];
byte[] allocation2 = new byte[2 * _1MB];
byte[] allocation3 = new byte[2 * _1MB];
byte[] allocation4 = new byte[4 * _1MB];
Client模式下
[GC [DefNew: 6487K-&194K(9216K), 0.0042856 secs] 6487K-&K), 0.0043281 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
def new generation
total 9216K, used 4454K [0x00, 0xb80000)
eden space 8192K,
52% used [0x8, 0x0000)
from space 1024K,
18% used [0xa810, 0xb80000)
space 1024K,
0% used [0x0, 0xa80000)
tenured generation
total 10240K, used 6144K [0xb80, 0x0000)
the space 10240K,
60% used [0xb88, 0x0)
compacting perm gen
total 21248K, used 2982K [0x00, 0x0000)
the space 21248K,
14% used [0x0, 0x9a00, 0xa40000)
No shared spaces configured.
Server模式下
PSYoungGen
total 9216K, used 6651K [0xaf2000, 0x0000)
eden space 8192K, 81% used [0xafef70,0x0000)
from space 1024K, 0% used [0x000,0x0000)
space 1024K, 0% used [0x000,0x0000)
total 10240K, used 4096K [0x000, 0xaf20000)
object space 10240K, 40% used [0x018,0xaf20000)
total 21248K, used 2972K [0x0, 0x0000)
object space 21248K, 13% used [0x8,0xe0000)
看到在Client模式下,最后分配的4M在新生代中,先分配的6M在老年代中;在Server模式下,最后分配的4M在老年代中,先分配的6M在新生代中。说明不同的垃圾收集器组合对于对象的分配是有影响的。讲下两者差别的原因:
1、Client模式下,新生代分配了6M,虚拟机在GC前有6487K,比6M也就是6144K多,多主要是因为TLAB和EdenAllocationTest这个对象占的空间,TLAB可以通过&-XX:+PrintTLAB&这个虚拟机参数来查看大小。OK,6M多了,然后来了一个4M的,Eden+一个Survivor总共就9M不够分配了,这时候就会触发一次Minor GC。但是触发Minor GC也没用,因为allocation1、allocation2、allocation3三个引用还存在,另一块1M的Survivor也不够放下这6M,那么这次Minor GC的效果其实是通过分配担保机制将这6M的内容转入老年代中。然后再来一个4M的,由于此时Minor GC之后新生代只剩下了194K了,够分配了,所以4M顺利进入新生代。
2、Server模式下,前面都一样,但是在GC的时候有一点区别。在GC前还会进行一次判断,如果要分配的内存&=Eden区大小的一半,那么会直接把要分配的内存放入老年代中。要分配4M,Eden区8M,刚好一半,而且老年代10M,够分配,所以4M就直接进入老年代去了。为了验证一下结论,我们把3个2M之后分配的4M改为3M看一下
public class EdenAllocationTest
private static final int _1MB = 1024 * 1024;
public static void main(String[] args)
byte[] allocation1 = new byte[2 * _1MB];
byte[] allocation2 = new byte[2 * _1MB];
byte[] allocation3 = new byte[2 * _1MB];
byte[] allocation4 = new byte[3 * _1MB];
运行结果为
[GC [PSYoungGen: 6487K-&352K(9216K)] 6487K-&K), 0.0035661 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC [PSYoungGen: 352K-&0K(9216K)] [PSOldGen: 6144K-&K)] 6496K-&K) [PSPermGen: 2941K-&K)], 0.0035258 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
PSYoungGen
total 9216K, used 3236K [0xaf4000, 0x0000)
eden space 8192K, 39% used [0xaf018,0x0000)
from space 1024K, 0% used [0x000,0x0000)
space 1024K, 0% used [0x000,0x0000)
total 10240K, used 6338K [0x000, 0xaf40000)
object space 10240K, 61% used [0x858,0xaf40000)
total 21248K, used 2982K [0x0, 0x0000)
object space 21248K, 14% used [0x0,0x0000)
看到3M在新生代中,6M通过分配担保机制进入老年代了。
大对象直接进入老年代
虚拟机参数为&-XX:+PrintGCDetails -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728&,最后那个参数表示大于这个设置值的对象直接在老年代中分配,这样做的目的是为了避免在Eden区和两个Survivor区之间发生大量的内存复制。测试代码为
public class OldTest
private static final int _1MB = 1024 * 1024;
public static void main(String[] args)
byte[] allocation = new byte[4 * _1MB];
Client模式下
def new generation
total 9216K, used 507K [0x00, 0xb40000)
eden space 8192K,
6% used [0xbef28, 0x0000)
from space 1024K,
0% used [0x0, 0xa40000)
space 1024K,
0% used [0xa400, 0xb40000)
tenured generation
total 10240K, used 4096K [0xb40, 0x0000)
the space 10240K,
40% used [0xb418, 0xf40)
compacting perm gen
total 21248K, used 2972K [0x00, 0x0000)
the space 21248K,
13% used [0xa0, 0x00)
No shared spaces configured.
Server模式下
PSYoungGen
total 9216K, used 4603K [0xafccc0000)
eden space 8192K, 56% used [0xafcef40,0xc0000)
from space 1024K, 0% used [0xccc0000)
space 1024K, 0% used [0xccc0000)
total 10240K, used 0K [0xc000000afc000000afc0000)
object space 10240K, 0% used [0xcc000000afc0000)
total 21248K, used 2972K [0xc0, 0xc0000)
object space 21248K, 13% used [0xca0,0x0000)
看到Client模式下4M直接进入了老年代,Server模式下4M还在新生代中。产生这个差别的原因是&-XX:PretenureSizeThreshold&这个参数对Serial+Serial Old垃圾收集器组合有效而对Parallel+Serial Old垃圾收集器组合无效。
其他几条原则
上面列举的原则其实不重要,只是演示罢了,也不需要记住,因为实际过程中我们可能使用的并不是上面的垃圾收集器的组合,可能使用ParNew垃圾收集器,可能使用G1垃圾收集器。场景很多,重要的是要在实际使用的时候有办法知道使用的垃圾收集器对于对象分配有哪些原则,因为理解这些原则才是调优的第一步。下面列举一下对象分配的另外两条原则:
1、长期存活的对象将进入老年代。Eden区中的对象在一次Minor GC后没有被回收,则对象年龄+1,当对象年龄达到&-XX:MaxTenuringThreshold&设置的值的时候,对象就会被晋升到老年代中
2、Survivor空间中相同年龄的所有对象大小总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到&-XX:MaxTenuringThreshold&设置要求的年龄
阅读(...) 评论()

我要回帖

更多关于 8g虚拟机分配多少内存 的文章

 

随机推荐