(原创)关于Tomcat默认最大占用内存的一系列问题

今天舍友问我tomcat啥都不改的话,内存最大能占用到多少,我脱口而出四分之一物理内存,然而事实真的是这样吗?

1、查阅官方说明文档

JDK文档传送门,当然全是英文看起来需要一点时间,所以我找来了别人的大概翻译

client模式下,JVM初始和最大堆大小为:
在物理内存达到192MB之前,JVM最大堆大小为物理内存的一半,否则,在物理内存大于192MB,在到达1GB之前,JVM最大堆大小为物理内存的1/4,大于1GB的物理内存也按1GB计算,举个例子,如果你的电脑内存是128MB,那么最大堆大小就是64MB,如果你的物理内存大于或等于1GB,那么最大堆大小为256MB。
Java初始堆大小是物理内存的1/64,但最小是8MB。

server模式下:
与client模式类似,区别就是默认值可以更大,比如在32位JVM下,如果物理内存在4G或更高,最大堆大小可以提升至1GB,,如果是在64位JVM下,如果物理内存在128GB或更高,最大堆大小可以提升至32GB。

2、实践是校验真理的唯一标准

在Linux下,我们可以通过以下命令来查看堆内存的默认最大值

java -XX:+PrintFlagsFinal -version | grep MaxHeapSize

输出如下:

上图是我在2G RAM的虚拟机里的结果,可以看到522190848 Byte = 498 MB,而这虚拟机除了给硬件保留的内存外总内存空间为 2039552 KB ,其四分之一也就是509888KB= 497MB,可以说理论和实际值非常接近,误差可忽略。

然而在Windows上,我没有通过命令行去找出最大堆,而是选择了写个Java类来获取。

package test;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;

public class MaxHeapTest {
    public static void main(String[] args) {
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();  
        MemoryUsage heapMemory= memoryMXBean.getHeapMemoryUsage();  
        long maxHeap = heapMemory.getMax();
        // 单位:字节
        System.out.println(maxHeap +" byte");
    }
}

输出为:

有趣的地方来了,根据换算,3771203584 byte = 3596.5 MB,而我windows的机器上除去硬件保留的内存,可用的有 16178MB,其四分一为 4044.5 MB,和上图的输出值存在不可忽略的大小差异,难道是windows系统特别些?我们继续探索。

3、探索、求真

上述的获取方式不一样,我们先来统一下方式,

在CMD下以同样的方式打印JVM参数,找MaxHeapSize字样:

其中,4242538496 byte = 4046MB,嗯,这个值就才对嘛。

那么现在的问题转变成了,为啥通过java编程方式获取到的最大堆内存会和真实值有出入的?小弟不才,没有了进一步探索的毅力,只想站在前辈的肩膀上看看风景。

这里是别人留下的宝贵经验:传送门2
经过计算和检验,的确差了一个Survivor区的大小,加上这个也就对得上了。

引用别人的结论:通过MemoryMXBean 获取的最大堆内存,应该是JVM可以使用的最大堆内存。因为两个幸存区,采用的是标记拷贝的GC算法,实际分配时,永远有一个幸存区是不能被使用的,所以最大堆内存没有包含这一部分

结尾:

以上结论仅针对Hot Spot虚拟机,其它JVM请自行实验。

本文参考链接:https://segmentfault.com/q/1010000007235579?_ea=1280245

发表评论