博客详情

JVM中的内存结构 (原创)

作者: 朝如青丝暮成雪
发布时间:2020-04-29 17:31:14  文章分类:JVM   阅读(1457)  评论(0)

JVM的内存结构:
程序计数器、虚拟机栈、本地方法栈、堆、方法区


一、程序计数器(寄存器):
   Program Counter Register
    每个线程都有一个程序计数器,用来记住线程中下一条指令的地址 (cpu往往是多线程并发执行的,因此cpu会在多个线程中来回切换)
    (1)不存在内存泄漏问题
    (2)每个线程独有

二、虚拟机栈(Java Virtual Machine Stack)
   虚拟机栈内存是每个线程运行需要的总内存,可以再java命令中通过-Xss参数来指定,对于linux、macOS等系统来说默认值都是1024k,对应windows系统则取决于系统的虚拟内存大小。
   虚拟机栈由多个栈桢组成,每个栈桢都是线程中一次方法调用所需要的内存,当前处于栈顶的栈桢也称为活动栈桢。线程中位于栈顶的栈桢只有一个,也就是说活动栈桢只有一个。

   栈桢(Frame)中的内存包含:方法中参数、局部变量、返回值地址。

   虚拟机栈:
   (1)可能会出现内存溢出问题
      a、栈桢数量太多导致内存溢出
      b、栈桢占用内存太多导致内存溢出
   
   (2)java程序cpu内存占用太高,问题排查
     a、top 命令
     b、ps H -eo pid,tid,%cpu 命令,查看进程中都有哪些线程
     c、jstack 命令 ,列出java中进程的线程信息
   
   (3)线程安全问题
      a、栈桢中的局部变量,如果没有被方法返回,则不会有线程安全问题
      b、栈桢中的局部变量,如果被方法返回了,并且返回值是个引用数据类型,则可能导致线程安全问题
      c、栈桢中的参数,如果传入的是引用数据类型,则可能导致线程安全问题

   (4)栈中声明的变量,当超过变量的作用范围后,Java会自动释放掉为该变量所分配的内存空间 




问题辨析:
1、垃圾回收是否涉及栈内存?
解答:不涉及,因为线程中栈桢是随着方法出栈而自动释放,不涉及垃圾回收。

2、栈内存分配越大越好吗?
解答:不是。栈内存过大意味着java程序中单个线程的内存过大,这样会影响整个java程序的线程并发数。

3、方法内的局部变量是否是线程安全的?
解答: 方法中的局部变量如果被方法返回了,并且返回值是一个引用数据类型,则可能导致线程安全问题。

三、本地方法栈 (Native Method Stack)
 java为操作系统相关方法的执行所分配的内存,在java的JDK中存在大量的本地方法,如
   Object中的clone、hashcode、wait、notify、notifyAll等都是的。
   public native int hashCode();
   public final native void notify();
   public final native void notifyAll();
   public final native void wait(long timeout) throws InterruptedException;
  
   本地方法都是与操作系统底层相关的api方法,java中不提供实现,而由操作系统来实现(c、c++的代码)


四、堆(Heap) 

java中堆内存是jvm中管理的最大的一块内存区域, 用来存放所有的引用对象,堆也是垃圾回收器工作的区域。


命令工具: 
1、jps 查看java进程,  
2、jmap -heap [进程id]  查看某个时刻java进程内存快照 、
3、jconsole(图形化)
4、jvisualvm(图形化) 

栈是线程私有的,堆是多个线程共享的



五、方法区(Method Area)
JVM规范中定义的方法区,只是一个逻辑的概念,在具体不同厂商和版本的实现中可能都有不同。
以Oracle的hotspot虚拟机为例,在JDK1.6中的方法区是通过堆中的PermGen(永久代)来实现的,
而JDK1.8中的方法区是通过Metaspace(元空间)来实现的。


运行时常量池;
1、常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
2、运行时常量池,常量池是class文件中的,当该类被加载,他的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址


JDK1.6中的方法区中包含: class、classloader、运行期常量池(含StringTable)
JDK1.8中的方法区中包含: class、classloader、运行期常量池(其中的StringTable移到了堆中)


在JDK1.8中调用s.intern(),尝试将字符串放入串池中,返回值(s2)永远是指向串池中的地址,
如果放入成功了则s也指向了串池中的地址,没有放入成功则s还是执行原来的地址(堆中对象)

在JDK1.6中调用s.intern(),尝试将字符串放入串池中,返回值(s2)永远是指向常量池中的地址,
如果放入成功了,s会复制一份放入串池,s的指向不变(仍然是堆中对象)。没有放入成功s还是原来的地址。



JDK中设置元空间的大小 

(1)JDK8中设置元空间内存最大值,默认是无限大(取决于操作系统内存)
-XX:MaxMetaspaceSize=128m #JDK8中设置最大的元内存空间128兆

(2)JDK7中设置永久代堆内存大小
-XX:MaxPermSize=128m  

 
-Xss1m  指定栈的内存大小
-Xms128m  指定堆内存最小值    
-Xmx256m   指定堆内存最大值
-XX:StringTableSize=2000  #增加桶的数量使StringTable性能增加

关键字:  jvm    
评论信息
暂无评论
发表评论

亲,您还没有登陆,暂不能评论哦! 去 登陆 | 注册

博主信息
   
数据加载中,请稍候...
文章分类
   
数据加载中,请稍候...
阅读排行
 
数据加载中,请稍候...
评论排行
 
数据加载中,请稍候...

Copyright © 叮叮声的奶酪 版权所有
备案号:鄂ICP备17018671号-1

鄂公网安备 42011102000739号