jvm启动参数不会消耗在-Xms中声明的内存
在运行java应用程序之前,我的linux可用内存是4942356kb。我的启动参数使用-Xmx2048m和-Xms2048m将最大堆内存和初始堆内存设置为2G。但我发现在启动应用程序后,我的linux系统可用内存正在逐步减少,而不是启动后消耗的2G内存。有人能告诉我原因和机制吗 还有一个问题,我的jvm启动参数是jvm启动参数不会消耗在-Xms中声明的内存,jvm,out-of-memory,jvm-hotspot,Jvm,Out Of Memory,Jvm Hotspot,在运行java应用程序之前,我的linux可用内存是4942356kb。我的启动参数使用-Xmx2048m和-Xms2048m将最大堆内存和初始堆内存设置为2G。但我发现在启动应用程序后,我的linux系统可用内存正在逐步减少,而不是启动后消耗的2G内存。有人能告诉我原因和机制吗 还有一个问题,我的jvm启动参数是-XX:+PrintGCDetails-Xss512k-Xmx4096m-Xms4096m-Xloggc:gc_01221859.log,启动前的可用内存是5G,1小时后,我发现这个应
-XX:+PrintGCDetails-Xss512k-Xmx4096m-Xms4096m-Xloggc:gc_01221859.log
,启动前的可用内存是5G,1小时后,我发现这个应用程序已经有14053个线程了。显然,14053*512k=7195136=7.2G堆栈内存被消耗,明显大于系统可用内存和总内存。有人能为我解释一下吗
当thred的数量为14053时,我使用jcmd VM.native_memory summary
转储本机内存,它输出:
Native Memory Tracking:
Total: reserved=8688195KB, committed=8645103KB
- Java Heap (reserved=2181632KB, committed=2181632KB)
(mmap: reserved=2181632KB, committed=2181632KB)
- Class (reserved=5997KB, committed=5997KB)
(classes #8918)
(malloc=5997KB, #7418)
- Thread (reserved=5815888KB, committed=5815888KB)
(thread #11139)
(stack: reserved=5752332KB, committed=5752332KB)
(malloc=35936KB, #44562)
(arena=27620KB, #22266)
- Code (reserved=51385KB, committed=8293KB)
(malloc=1465KB, #2689)
(mmap: reserved=49920KB, committed=6828KB)
- GC (reserved=85657KB, committed=85657KB)
(malloc=5889KB, #155)
(mmap: reserved=79768KB, committed=79768KB)
- Compiler (reserved=294KB, committed=294KB)
(malloc=196KB, #400)
(arena=98KB, #2)
- Internal (reserved=52609KB, committed=52609KB)
(malloc=52609KB, #283728)
- Symbol (reserved=13406KB, committed=13406KB)
(malloc=10010KB, #98269)
(arena=3396KB, #1)
- Memory Tracking (reserved=301363KB, committed=301363KB)
(malloc=301363KB, #28396)
- Pooled Free Chunks (reserved=179933KB, committed=179933KB)
(malloc=179933KB)
- Unknown (reserved=32KB, committed=32KB)
(mmap: reserved=32KB, committed=32KB)
最高输出:
top - 18:47:02 up 397 days, 26 min, 1 user, load average: 3.68, 3.46, 2.58
Tasks: 164 total, 1 running, 163 sleeping, 0 stopped, 0 zombie
Cpu(s): 2.5%us, 0.6%sy, 0.0%ni, 96.7%id, 0.1%wa, 0.0%hi, 0.0%si, 0.1%st
Mem: 5990984k total, 5144764k used, 846220k free, 11564k buffers
Swap: 0k total, 0k used, 0k free, 132748k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
11513 wuchang 20 0 12.1g 3.7g 15m S 188.8 64.9 41:46.42 java
1264 wuchang 20 0 17116 1240 900 R 1.8 0.0 0:00.02 top
1 root 20 0 19232 364 84 S 0.0 0.0 0:02.57 init
自由输出为:
total used free shared buffers cached
Mem: 5990984 5786744 204240 0 12012 134092
-/+ buffers/cache: 5640640 350344
Swap: 0 0 0
我编写了一个非常简单的java代码,其工作就是创建线程:
public class CreateThread {
private static int threadNumber = 0;
public static void main(String[] args) {
while (true) {
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Thread " + threadNumber++);
while(true){
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
}
}
}
将其包装到jar文件后,在linux中运行它,如下所示:
java -Xss512k -jar CreateThread.jar > /home/wuchang/test
报告前共创建了32313个线程:
Exception in thread "main" 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 CreateThread.main(CreateThread.java:22)
还有一个问题,为什么错误是OutOfMemory而不是StackOverflow
我的linux系统线程的一些限制是:
[wuchang@hz10-45-88 ~]$ cat /proc/sys/kernel/threads-max
93335
[wuchang@hz10-45-88 ~]$ ulimit -u
46667
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
更新1: 每次我用不同的启动参数运行java应用程序时,包括三种情况:
case 1: -Xss512k -Xmx4096m -Xms4096m
case 2: -Xss512k -Xmx2048m -Xms2048m
case 3: -Xss256k -Xmx2048m -Xms2048m
当然,由于我的应用程序是一个压力测试,经过一段时间后,该进程将被操作系统自动终止。我已将终止前的最后一个top输出记录在下面:
case 1:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18136 wuchang 20 0 15.4g 4.3g 6456 S 99.3 75.0 36:16.22 java
case 2:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9689 wuchang 20 0 15.0g 4.2g 14m S 98.6 73.0 55:36.62 java
case 3:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
8237 wuchang 20 0 10.1g 4.2g 9284 S 100.6 74.0 60:43.95 java
每次运行启动进程之前,系统可用内存约为5G。
从以上输出来看,每次VIRT变化很大,但RES保持大致相同。因此,我的补充问题是:
更新2: 我已经运行了@Ivan提供的代码:
public class CreateThread {
private static int threadNumber = 0;
public static int doRecursiveCall(int i) throws StackOverflowError {
return doRecursiveCall(++i);
}
public static void warmupNativeStack() {
int sideEffect;
try {
sideEffect = doRecursiveCall(0);
} catch (StackOverflowError ignored) {
return;
}
System.out.println("Never happend " + sideEffect);
}
public static void main(String[] args) {
while (true) {
new Thread(new Runnable() {
@Override
public void run() {
warmupNativeStack();
System.out.println("Thread " + threadNumber++);
while (true) {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}).start();
}
}
}
结果是:
java -Xss512k -Xmx2048m -Xms2048m -jar CreateThread.jar
totally 8600 threads are created.before exit,the max VIRT is 8.7G and max RSS is 4.5G
java -Xss256k -Xmx2048m -Xms2048m -jar CreateThread.jar
totally 16780 threads are created.before exit,the max VIRT is 8.7G and max RSS is 4.6G
实验表明,在这种情况下,max-VIRT和RSS与jvm启动参数无关,它们保持不变。因此,根据UPDATE 1和UPDATE 2的实验结果,我猜,我的应用程序被操作系统杀死的原因可能是RSS内存已达到极限,默认情况下,JVM在应用程序执行期间增量地接触页面,而不是线程数或总VIRT,对吗。要更改此行为,请使用以下选项
-XX:+AlwaysPreTouch
。
在这种情况下,JVM在JVM初始化期间预先接触堆(堆的每一页都按需归零)
也别忘了
更新:
很可能您的内存已交换。您可以使用检查交换使用情况
更新2:
在这种情况下,你正在处理。内存过度使用是操作系统的一项功能,它允许使用比物理机器实际拥有的内存空间更多的内存空间。本机堆栈非常懒惰,可以避免触及备份的物理内存。因此,您有12.1g的虚拟内存和3.7g的内存。要进行更详细的分析,可以使用pmap-x
我编写了一个非常简单的java代码,其工作就是创建线程
这就是为什么没有使用分配给线程的内存。只是在你的线程中做一些有用的事情。例如:
public class CreateThread {
private static int threadNumber = 0;
public static int doRecursiveCall(int i) throws StackOverflowError {
return doRecursiveCall(++i);
}
public static void warmupNativeStack() {
int sideEffect;
try {
sideEffect = doRecursiveCall(0);
} catch (StackOverflowError ignored) {
return;
}
System.out.println("Never happend " + sideEffect);
}
public static void main(String[] args) {
while (true) {
new Thread(new Runnable() {
@Override
public void run() {
warmupNativeStack();
System.out.println("Thread " + threadNumber++);
while (true) {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}).start();
}
}
}
为什么错误是OutOfMemory而不是StackOverflow
事实上,你很幸运,因为在现实生活中,一切都以失败告终。在您的情况下,通过操作系统请求一个新的Java线程。操作系统试图创建一个新的本机线程,该线程需要为该线程分配内存,但由于缺少物理内存,分配失败。在现实生活中,您的java应用程序将被Linux内核(OOM killer)杀死。
堆栈溢出用于其他目的(请参阅)
在我的进程被操作系统终止之前,MAX-VIRT依赖于什么
看
如何根据启动前的操作系统可用内存和JVM启动参数估计最大线程数
这取决于许多因素,JVM参数不起任何作用。例如,您必须知道应用程序中调用堆栈的最大深度-使用您和我的示例检查线程数 请提供JVM本机内存跟踪器的信息。您好,已经提供了jcmd OUTPOUT。我只是不明白为什么堆栈内存总量会大于系统可用内存。非常感谢。请不要频繁更改问题。这很难读。如果您有新问题-创建新问题,而不是更改此问题好的。非常感谢您的帮助。我已将您的代码输出更新为更新2。有什么建议吗?查看OOM killer日志,很难说清楚。谢谢。我使用top命令,这表明我的交换大小为零。然后,什么会使我的堆栈总内存大于系统总可用内存?请提供“空闲”和内部内存使用情况的输出
-XX:NativeMemoryTracking=summary
。非常感谢您的详细解释。我在更新1中有一些额外的描述。在我运行进程,根据启动前的系统可用内存和jvm启动参数?