java的java 默认内存的内存空间是多少

Java内存空间划分初识(适合初学者)_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
Java内存空间划分初识(适合初学者)
上传于||文档简介
&&在编程中,自己归纳过那些变量会被初始化,可是全靠死记硬背,实际开发中还老犯错。后来在看书中,了解是自己对Java内存空间的认识不足。今天通过看视频,查阅书籍和网上查询,简单归纳了Java内存空间划分,使得自己对Java数据分类和运用有很大的提升。现在分享给大家,希望能有一些帮助和启发。
阅读已结束,如果下载本文需要使用0下载券
想免费下载更多文档?
定制HR最喜欢的简历
下载文档到电脑,查找使用更方便
还剩1页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢java中三种常见内存溢出错误的处理方法 - 为程序员服务
为程序员服务
java中三种常见内存溢出错误的处理方法
相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识。
在解决java内存溢出问题之前,需要对jvm(java虚拟机)的内存管理有一定的认识。jvm管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域)、Heap space(堆区域)、Java Stacks(Java栈)。其中永久保存区域主要存放Class(类)和Meta的信息,Class第一次被Load的时候被放入PermGen space区域,Class需要存储的内容主要包括方法和静态属性。堆区域用来存放Class的实例(即对象),对象需要存储的内容主要是非静态属性。每次用new创建一个对象实例后,对象实例存储在堆区域中,这部分空间也被jvm的垃圾回收机制管理。而Java栈跟大多数编程语言包括汇编语言的栈功能相似,主要基本类型变量以及方法的输入输出参数。Java程序的每个线程中都有一个独立的堆栈。容易发生内存溢出问题的内存空间包括:Permanent Generation space和Heap space。
第一种OutOfMemoryError: PermGen space
发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generation space有关。解决这类问题有以下两种办法:
增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。如针对tomcat6.0,在catalina.sh 或catalina.bat文件中一系列环境变量名说明结束处(大约在70行左右) 增加一行: JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m" 如果是windows服务器还可以在系统环境变量中设置。感觉用tomcat发布sprint+struts+hibernate架构的程序时很容易发生这种内存溢出错误。使用上述方法,我成功解决了部署ssh项目的tomcat服务器经常宕机的问题。
清理应用程序中web-inf/lib下的jar,如果tomcat部署了多个应用,很多应用都使用了相同的jar,可以将共同的jar移到tomcat共同的lib下,减少类的重复加载。这种方法是网上部分人推荐的,我没试过,但感觉减少不了太大的空间,最靠谱的还是第一种方法。
第二种OutOfMemoryError:
Java heap space
发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap space有关。解决这类问题有两种思路:
检查程序,看是否有死循环或不必要地重复创建大量对象。找到原因后,修改程序和算法。 我以前写一个使用K-Means文本聚类算法对几万条文本记录(每条记录的特征向量大约10来个)进行文本聚类时,由于程序细节上有问题,就导致了Java heap space的内存溢出问题,后来通过修改程序得到了解决。
增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m
第三种OutOfMemoryError:unable to create new native thread
在java应用中,有时候会出现这样的错误:OutOfMemoryError: unable to create new native thread.这种怪事是因为JVM已经被系统分配了大量的内存(比如1.5G),并且它至少要占用可用内存的一半。有人发现,在线程个数很多的情况下,你分配给JVM的内存越多,那么,上述错误发生的可能性就越大。
那么是什么原因造成这种问题呢?
每一个32位的进程最多可以使用2G的可用内存,因为另外2G被操作系统保留。这里假设使用1.5G给JVM,那么还余下500M可用内存。这500M内存中的一部分必须用于系统dll的加载,那么真正剩下的也许只有400M,现在关键的地方出现了:当你使用Java创建一个线程,在JVM的内存里也会创建一个Thread对象,但是同时也会在操作系统里创建一个真正的物理线程(参考JVM规范),操作系统会在余下的400兆内存里创建这个物理线程,而不是在JVM的1500M的内存堆里创建。在jdk1.4里头,默认的栈大小是256KB,但是在jdk1.5里头,默认的栈大小为1M每线程,因此,在余下400M的可用内存里边我们最多也只能创建400个可用线程。
这样结论就出来了,要想创建更多的线程,你必须减少分配给JVM的最大内存。还有一种做法是让JVM宿主在你的JNI代码里边。
给出一个有关能够创建线程的最大个数的估算公式:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
对于jdk1.5而言,假设操作系统保留120M内存:
1.5GB JVM: (2GB-1.5Gb-120MB)/(1MB) = ~380 threads
1.0GB JVM: (2GB-1.0Gb-120MB)/(1MB) = ~880 threads
对于栈大小为256KB的jdk1.4而言,
1.5GB allocated to JVM: ~1520 threads
1.0GB allocated to JVM: ~3520 threads
对于这个异常我们首先需要判断下,发生内存溢出时进程中到底都有什么样的线程,这些线程是否是应该存在的,是否可以通过优化来降低线程数; 另外一方面默认情况下java为每个线程分配的栈内存大小是1M,通常情况下,这1M的栈内存空间是足足够用了,因为在通常在栈上存放的只是基础类型的数据或者对象的引用,这些东西都不会占据太大的内存, 我们可以通过调整jvm参数,降低为每个线程分配的栈内存大小来解决问题,例如在jvm参数中添加-Xss128k将线程栈内存大小设置为128k。
推荐阅读:
相关聚客文章Java对象占用内存大小 - sunqi - ITeye技术网站
博客分类:
new Object()将占用多少bytes的内存空间?
原生类型(primitive type)的内存占用Primitive Type
Memory Required(bytes)—————————————————————boolean
对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
HotSpot虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。对象需要存储的运行时数据很多,其实已经超出了32、64位Bitmap结构所能记录的限度,但是对象头信息是与对象自身定义的数据无关的额 外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。例如在32位的HotSpot虚拟机 中对象未被锁定的状态下,Mark Word的32个Bits空间中的25Bits用于存储对象哈希码(HashCode),4Bits用于存储对象分代年龄,2Bits用于存储锁标志 位,1Bit固定为0,在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容如下表所示。
表1 HotSpot虚拟机对象头Mark Word
对象哈希码、对象分代年龄
指向锁记录的指针
轻量级锁定
指向重量级锁的指针
膨胀(重量级锁定)
空,不需要记录信息
偏向线程ID、偏向时间戳、对象分代年龄
1. 一个object header, 也称object overhead, 保存当前实例的type信息和内置monitor信息等, 32位系统上占用8bytes,64位系统上占用16bytes;2. 0到多个fields, reference类型在32位系统上每个占用4bytes, 在64位系统上每个占用8 primitive类型参考上面;3. padding, 对步骤1、2之和的补长。CPU从内存中读取数据是以word为基本单位, 32位的系统中word宽度为32bits, 64位的系统中word宽度为64bits, 将整个Java对象占用内存补长为word的整倍数大大提高了CPU存取数据的性能,。 就Hotspot而言,不管是32位系统还是64位系统要求(步骤1 + 步骤2 + padding) % 8等于0且0 &= padding & 8。例如在64位系统上:
public class Student {
如new Student()则其占用内存: 16 + 4 = 20,按照3中的说明则padding为4bytes,这样整个内存占用为24bytes。
六. 一维原生数组的内存占用——————————————————————————————–1. 在32位的系统中, 占用内存为: 型别占用内存 * 数组长度 + 8(数组在JVM中被当成特殊的对象, object overhead占用8bytes) + 4(数组长度) + padding。如:byte[2], 型别占用内存,即byte型别占用1byte,数组长度为2,这样占用的总内存为1 * 2 + 8 + 4 = 14,padding上2bytes为16bytes,所以byte[2]占用内存为16bytes。2. 在64位的系统中, 占用内存为: 型别占用内存 * 数组长度 + 16(object overhead占用16bytes) + 8(数组长度) + padding。如:byte[2], 型别占用内存,即byte型别占用1byte,数组长度为2,这样占用的总内存为1 * 2 + 16 + 8 = 26,padding上6bytes,26 + 6 = 32bytes,所以byte[2]占用内存为32bytes
七. 多维数组和一维对象数组——————————————————————————————–1. 在32位的系统中, 占用内存为: reference占用内存 * 数组第1维长度 +12(数组本身被当做reference占8bytes,数组长度占4bytes)。如:byte[3][7], reference占用内存4byte,数组第1维长度为3,这样占用的总内存为4 * 3 + 12 = 24,所以byte[3][7]占用内存为24bytes。再如byte[7][3], reference占用内存4byte,数组第1维长度为7,这样占用的总内存为4 * 7 + 12 = 40,所以byte[7][3]占用内存为40bytes。再如new HashMap[7][6][4],reference占用内存4byte,数组第1维长度为7,这样占用的总内存为4 * 7 + 12 = 40,所以HashMap[7][6][4]占用内存为40bytes。2. 在64位的系统中, 占用内存为: reference占用内存 * 数组第1维长度 +24(数组本身被当做reference占16bytes,数组长度占8bytes)。如:byte[3][7], reference占用内存8byte,数组第1维长度为3,这样占用的总内存为8 * 3 + 24 = 48,所以byte[3][7]占用内存为48bytes。
八. 编码计算——————————————————————————————–1. java.lang.instrument.Instrumentation实例由JVM产生,我们需实现一个代理(agent),根据java.lang.instrument的package specification说明,这个代理里需有个public static void premain(String agentArgs, Instrumentation inst); 方法,这样在JVM初始化后在调用应用程序main方法前,JVM将调用我们agent里的这个premain方法,这样就注入了Instrumentation实例。2. 计算实例的内存大小,通过Instrumentation#getObjectSize(Object objectToSize)获得。3. 注意: 如果有field是常量(如, Boolean.FALSE),因为多实例共享,所以算其占用内存为0。4. 如计算对象Deep范围内存占用的话则需递归计算引用对象占用的内存,然后进行累加。5. 代码实现如下MemoryCalculator.java:
* @(#)MemoryCalculator.java 1.0
* Copyright 2010 Richard Chen(utopia_rabbi@sse.) All Rights Reserved.
* PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
package charpter.
import java.lang.instrument.I
import java.lang.reflect.A
import java.lang.reflect.F
import java.lang.reflect.M
import java.util.IdentityHashM
import java.util.M
import java.util.S
* 提供实例占用内存大小的计算功能. 内部借助JVM的{@link Instrumentation}实现.
* @author Rich, .
* @version 1.0
* @since 1.0
public final class MemoryCalculator {
* JVM在初始化后在调用应用程序main方法前将调用本方法, 本方法中可以写任何main方法中可写的代码.
* @param agentArgs 命令行传进行来的代理参数, 内部需自行解析.
* @param inst JVM注入的句柄.
public static void premain(String agentArgs, Instrumentation inst) {
instrumentation =
* 计算实例本身占用的内存大小. 注意:
* 1. 多次调用可能结果不一样, 主要跟实例的状态有关
* 2. 实例中成员变量如果是reference类型, 则reference所指向的实例占用内存大小不统计在内
* @param obj 待计算内存占用大小的实例.
* @return 内存占用大小, 单位为byte.
public static long shallowSizeOf(Object obj) {
if (instrumentation == null) {
throw new IllegalStateException("Instrumentation initialize failed");
if (isSharedObj(obj)) {
return instrumentation.getObjectSize(obj);
* 计算实例占用的内存大小, 含其成员变量所引用的实例, 递归计算.
* @param obj 待计算内存占用大小的实例.
* @return 内存占用大小, 单位为byte.
public static long deepSizeOf(Object obj) {
Map calculated = new IdentityHashMap();
Stack unCalculated = new Stack();
unCalculated.push(obj);
long result = 0;
result += doSizeOf(unCalculated, calculated);
} while (!unCalculated.isEmpty());
* 判断obj是否是共享对象. 有些对象, 如interned Strings, Boolean.FALSE和Integer#valueOf()等.
* @param obj 待判断的对象.
* @return true, 是共享对象, 否则返回false.
private static boolean isSharedObj(Object obj) {
if (obj instanceof Comparable) {
if (obj instanceof Enum) {
} else if (obj instanceof String) {
return (obj == ((String) obj).intern());
} else if (obj instanceof Boolean) {
return (obj == Boolean.TRUE || obj == Boolean.FALSE);
} else if (obj instanceof Integer) {
return (obj == Integer.valueOf((Integer) obj));
} else if (obj instanceof Short) {
return (obj == Short.valueOf((Short) obj));
} else if (obj instanceof Byte) {
return (obj == Byte.valueOf((Byte) obj));
} else if (obj instanceof Long) {
return (obj == Long.valueOf((Long) obj));
} else if (obj instanceof Character) {
return (obj == Character.valueOf((Character) obj));
* 确认是否需计算obj的内存占用, 部分情况下无需计算.
* @param obj 待判断的对象.
* @param calculated 已计算过的对象.
* @return true, 意指无需计算, 否则返回false.
private static boolean isEscaped(Object obj, Map calculated) {
return obj == null || calculated.containsKey(obj)
|| isSharedObj(obj);
* 计算栈顶对象本身的内存占用.
* @param unCalculated 待计算内存占用的对象栈.
* @param calculated 对象图谱中已计算过的对象.
* @return 栈顶对象本身的内存占用, 单位为byte.
private static long doSizeOf(Stack unCalculated, Map calculated) {
Object obj = unCalculated.pop();
if (isEscaped(obj, calculated)) {
Class clazz = obj.getClass();
if (clazz.isArray()) {
doArraySizeOf(clazz, obj, unCalculated);
while (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!Modifier.isStatic(field.getModifiers())
&& !field.getType().isPrimitive()) {
field.setAccessible(true);
unCalculated.add(field.get(obj));
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
clazz = clazz.getSuperclass();
calculated.put(obj, null);
return shallowSizeOf(obj);
* 将数组中的所有元素加入到待计算内存占用的栈中, 等待处理.
* @param arrayClazz 数组的型别.
* @param array 数组实例.
* @param unCalculated 待计算内存占用的对象栈.
private static void doArraySizeOf(Class arrayClazz, Object array,
Stack unCalculated) {
if (!arrayClazz.getComponentType().isPrimitive()) {
int length = Array.getLength(array);
for (int i = 0; i & i++) {
unCalculated.add(Array.get(array, i));
/** JVM将在启动时通过{@link #premain}初始化此成员变量. */
private static Instrumentation instrumentation =
,从Java SE 6u23之后的64位版本就默认打开了对象指针压缩。
十. Compressed oops的内存占用注意,Compressed oops只在64位的JVM中才会有,另外,在Java SE 6u23之前的1.6版本中需要通过-XX:+UseCompressedOops参数开启。压缩算法对对象内存占用计算的影响主要在于:——————————————————————————————–1. object header,未压缩前由一个native-sized mark word 8bytes加上一个class word 8bytes组成,共16bytes。采用压缩后,class word缩减为4bytes,现共占用12bytes;2. reference类型,由8bytes缩减为4bytes;3. 数组长度,由8bytes缩减为4bytes。
所以,上述测试案例中:——————————————————————————————–1. 原生类型,内存占用大小不变。2. 对象类型,object header由16bytes变更为12bytes,reference类型的fields由8bytes变更为4bytes,primitive类型的fields保持不变,padding不变。3. 一维原生数组,如new byte[2]占用内存的计算公式由:型别占用内存 * 数组长度 + 16 + 8 + padding变更为: 型别占用内存 * 数组长度 + 12 + 4 + padding,这样得到: 1byte * 2 + 12 + 4 = 18,padding上6bytes等于24bytes。4. 多维数组和一维对象数组,如new byte[3][7],计算公式由: reference占用内存 * 数组第1维长度 +24(数组本身被当做reference占16bytes,数组长度占8bytes) 变更为: reference占用内存 * 数组第1维长度 + 16(object header 12bytes,数组长度占4bytes) + padding,这样得到:4bytes * 3 + 16 = 28,padding上4bytes等于32bytes。 再如new HashMap[7],7 * 4bytes + 16 = 44bytes,padding上4bytes为48bytes。
十一. 总结通过上述Java内存占用大小的理论分析与实际测试,给我们实际开发带来几点重要的启发:——————————————————————————————–1. 同样的程序在不同环境下运行,占用的内存不一样大小,64位系统上占用的内存要比在32位系统上多1至1.5倍;2. n个元素的数组要比n个单独元素占用更大的内存,特别是primitive类型的数组;3. 定义多维数组时,要尽可能把长度小的放在第1维,即int[9][1]要比int[1][9]占用更多内存,Integer[]远比Integer[3][4][1000]占用的内存要多得多;4. Java SE 6u23之后的64位版本要比之前的版本在对象内存占用方面小得多。
jvm对于对象会启用对齐优化,我们定义类时field的顺序在运行期会被打乱
关闭了压缩指针模式后,Person对象体偏移由 offset = 16变成了 offset = 12
所以开启压缩指针模式后,对象头的_klass域得到了压缩,居然变成了32位系统时的长度4字节了,我们都知道32位的长度最多只能表示4G的内存,那么HostSpot 究竟是如何处理的呢
我们引用官方文档:
这就是面对对象的好处,我们面对的最小地址单元不是byte,而是object,也就是说在jvm的世界里32位地址表示的不是4GB,而是4G个对象的指针,大概是32GB,解码过程就是把对象指针乘以8加上GC堆的初始地址就能得到操作系统本地64位地址了,编码过程相反
其中启用压指得有操作系统底层的支持:GC堆从虚拟地址0开始分配
进而我们可以得到压指面对的所有场景:
如果GC堆大小在4G以下,直接砍掉高32位,避免了编码解码过程
如果GC堆大小在4G以上32G以下,则启用UseCompressedOop
如果GC堆大小大于32G,压指失效(所以说服务器内存太大不好......)
考虑到内存对齐,Person对象开压指长度为32字节,不开为40字节
浏览 10595
浏览: 142213 次
来自: 杭州
顶!但是感觉10*60*1000,比较好,可以避免时间差问题。 ...
顶!但是感觉10*60*1000,比较好,可以避免时间差问题。 ...
顶!但是感觉10*60*1000,比较好,可以避免时间差问题。 ...
完全看不出哪里异步新特性了 新开一个线程 servlet3以下 ...
D:\install\tomcat-7.0.29\webapp ...2015年1月 扩充话题大版内专家分月排行榜第二
2014年12月 扩充话题大版内专家分月排行榜第三
2013年10月 总版技术专家分月排行榜第三
2014年3月 Java大版内专家分月排行榜第一2014年1月 Java大版内专家分月排行榜第一2013年12月 Java大版内专家分月排行榜第一2013年11月 Java大版内专家分月排行榜第一2013年10月 Java大版内专家分月排行榜第一
本帖子已过去太久远了,不再提供回复功能。

我要回帖

更多关于 java 默认内存大小 的文章

 

随机推荐