您的位置:首页 > 编程语言 > Java开发

java学习笔记-java jvm内存结构分析

2016-07-15 17:33 781 查看
转载自https://github.com/pzxwhc/MineKnowContainer/issues/24

JDK7 整体内存结构 和 堆内存结构
JDK8 内存结构变动
JDK7 堆内存和非堆内存参数
实际优化


JDK7 整体内存结构

以下结构针对 JDK7,整体结构图如下:





如图,JVM内存区域分为 PC寄存器,JVM方法栈,本地方法栈,JVM方法区,JVM堆。

PC寄存器:存放下一条指令在方法中的偏移量。也可以看做是线程所执行的字节码的行号指示器,字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的指令。
JVM方法栈(JVM虚拟机栈):PC寄存器,JVM方法栈,和本地方法栈都是线程私有。在JVM方法栈有,有局部变量区和操作数区。人们经常说的JVM内存分为堆内存和栈内存(这 是一个比较笼统的说法)中的栈内存也就是指这个JVM方法栈的内存。当线程请求的栈的深度大于虚拟机所允许的深度时,就会抛出StackOverFlow 异常;如果虚拟机栈可以动态扩展,当扩展的时候无法申请足够的内存,会抛出OutOfMemory异常。
本地方法栈:主要用来支持native方法,记录native方法调用的状态。可以把native 方法看成是 java 调用 非 java 代码的一个接口。主要用于允许Java 和其他语言,比如 C 语言进行交互。
JVM方法区:主要存储已经加载的类的信息,比如构造函数的信息,方法的信息,常量的信息。Class对象提供的getXXX()方法取得的类的信息就是从JVM方法区中得到。Ps:JVM方法区是永久代的一个子集,常量池也是放在JVM方法区中。
JVM堆:主要目的是用来存放数组和对象。同时,JVM 堆也是 内存溢出和垃圾回收的主要区域。


JDK7 堆内存结构

堆内存结构图如下:





如图,在JVM堆中,又分为新生代,老生代。

可以看到,新生代中,又分为eden区域和两个Survivor区域。默认比例为8:1:1,也就是说,可以用的内存为90%。当然,可以用-XX:SurvivorRatio设置eden和Survivor的比值,默认为8:1。

特别注意:最右侧的永久代在JDK8中已经不在存在,被元空间代替metaspace所代替。另外,在jdk7中,字符串内部池就已经从永久代中移除。


JDK8 内存结构变动

在JDK8中,最主要的就是元空间代替了永久代(PermGen Space),由于 上面结构图中的 JVM方法区是永久代的子集,那么就是说这部分会没有了,取而代之的是元空间(Metaspace)。这里有一篇比较好的文章介绍了元空间:点这里

主要的意义在于:Metaspace 的内存大小可以动态增长,仅受限于本地内存大小。 当然,

-XX:MetaspaceSize
-XX:MaxMetaspaceSize
可以设定大小。


Java 堆内存和非堆内存参数

-XX:Persize:设置非堆内存初始值,默认为1/64。
-XX:MaxPersize:设置非堆内存最大值,默认为1/4.
-Xss:设置每个线程占用堆内存大小,现在默认为1M,以前为256K。设置线程越小,堆内存大小不变,可以创造的线程数越多(当然有一个限度)。但是这个值也需要经过严格的测试再设置。
-Xms:JVM堆初始分配内存。默认为物理内存的1/64,当默认堆内存的空余空间小于40%的时候,这个堆内存就会自动增长到-Xmx指定的最大堆分配内存。
-Xmx :JVM的最大堆分配内存。默认为物理内存的1/4,当空余内存大于70%的时候,该堆内存又会自动减少到-Xms指定的内存。
-Xmn:指定新生代的内存大小。当堆大小不变的情况下,新生代越大,老生代越小,默认新生代和老年代的比例为1:2,而且这个比例会严重影响系统性能,sun推荐为新生代占整个堆内存3/8。
-XX:SurvivorRatio:新生代中,又分为eden区域和两个Survivor区域。默认比例为8:1:1,也就是说,可以用的内存为90%。该参数用来设置eden和Survivor的比值,默认为8:1。
-XX:NewRatio:年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)


实际优化

内存是一个比较重要的东西,在自己开通了阿里云后,1G内存可是要30块钱左右,合理的规划内存意味着省钱。在查看自己阿里云内存时候的时候,发现:





上面有两个Java程序吃了比较多的内存。那么我的实际应用中,确实有一个Java后端程序,还有一个就是 Cassandra 数据库需要依赖到 Java。但是这个 Cassandra 数据库在我的实际部署中只是一个很小的辅助作用,但是内存占比这么高,显示是浪费内存了。

进入到 Docker 容器中的时候,
ps -ef | grep java
,如下:





此时可以看出 cassandra 启动的时候 java 分配了 1G 的内存,最后,便可以在对应的配置文件中修改,使得内存得以合理分配:





Ps:(后面 用了 Docker 的 cassandra 后,因为没镜像没提供参数,所以降不下来了。。。无语ing)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: