JVM的内存管理

JVM的内存管理---运行时数据区域。
 
 
    1.方法区(Method Area)
    2.堆(Heap)
 
    3.虚拟机栈(VM Stack)
    4.本地方法栈(Native Method Stack)
    5.程序计数器(Program Counter Register)

 
    方法区,堆  是所有线程共享的数据区域     虚拟机栈,本地方法栈,程序计数器 是线程隔离的数据区
 
    方法区:又名(静态区)是存放所有的class和static变量。
 
   堆:存储的是全部的对象,以及每个对象与之对应的class信息,不存储对象的引用,以及基本类型,只存放对象的本身。(所有对象存放在堆中)
    
   虚拟机栈:是线程私有的栈区,只保存基础数据类型,以及对象的引用(非对象实体)(存放的都是对象的引用)
 
   本地方法栈:存放单个线程运行时锁调用的本地方法(Native方法),当某个方法被大量调用时(取决于方法调用的次数),jvm会自动打包成本地方法,更加快速的读取运行。
 
   程序计数器:可以看做是当前线程执行的字节码的 行号指示器;  在多线程环境下,当cpu资源 从 ThreadA 突然被分配到  Thread B时,A会被暂时挂起。当A被再次执行时,cpu不会存储该线程上一时刻运行到哪一行,会从程序计数器中读取上次执行到的行数。
 

 
  • java虚拟机栈:每个方法在被调用时会创建一个栈帧,每一个方法从调用直至执行完成的过程,就对应着一个栈桢在虚拟机中入栈到出栈的过程(虚拟机栈大小配置  -Xss)
 
            栈:(先进后出)
  void A(){
    ....
    B()
  }
  void B(){
    .....
  }
当调用A方法时,会先在当前线程的虚拟机栈中,将A对象打包成一个栈桢,放入虚拟机栈,当调用的B对象的时候,将B对象打包成一个栈桢放入虚拟机栈。
 
 
  • java堆:是java虚拟机锁管理的内存中最大的一块,java堆是被所有线程共享的一块内存区域,对象实例都在这里分配内存,是垃圾收集器(GC)管理的主要区域                            (配置:-Xms[堆的最小值];        -Xmx[堆的最大值];        -Xmn)
 
 
  • 方法区:存储已被虚拟机加载的类信息,常量,静态变量,即时编辑器变异后的代码等数据,运行时常量池是方法区的一部分。
                        (-XX:PermSize[ 设置持久代(perm gen)初始值,物理内存的1/64];-XX:MaxPermSize[ 设置持久代最大值,物理内存的1/4];-XX:MetaspaceSize[元数据区的初始值];-XX:MaxMetaspaceSize[元数据区内存最大值])
 
  • 直接内存:直接内存(Direct Memory)不属于虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域。在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用native 函数库直接分配堆外内存,然后通脱一个存储在Java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据,直接内存的大小不受制于java堆的大小,而直接取决于物理机的内存大小。
 
 

 
JVM 内存溢出测试:
        场景一:Java堆溢出
            思路:堆中存放的是所有对象的实例,那就不停的new对象,撑爆他。
先配置堆内存大小
            
 
显而易见:当我们手动将堆内存设置比较小的时候,并且不停的往list中添加新的Object对象,最终压爆了堆内存,抛出了OOM(OutOfMemoryError),日志中还可以看到GC一直在对堆内存清除无引用的对象,表示我还可以再抢救一下,最终还是奈不过while(true)的压对象。
 
堆内存大小不变,换一种方式压爆他,创建一个100000000长度的String数组。
 
结论:日志打印了Java heap Space ,这意味着启动虚拟机,创建对象时,由于对内存太小,放不下一个亿长度的数组,直接抛出OOM。
总结一:
  1. 当抛出Java heap space 时可能是启动程序时堆内存太小,对象太大,启动报错。
  2. 当抛出GC overhead  limit exceeded时,可能时创建了较多的对象,比如出现死循环。
 
 
场景二:方法区和运行时常量池溢出
思路:启动虚拟机需要消耗一定的内存存放常量,因此我们手动设置元数据(也叫方法区,jdk1.8之后将方法区重新定义,命名为元数据)大小为4M
 
结论:当我们给定的元数据区太小,已经无法存放jvm启动时需要存放的类,常量时,直接报错MaxMetaspaceSize is too small,元数据区内存最大值太小。
 
场景三:虚拟机栈和本地方法栈溢出。
   思路:虚拟机栈是线程私有的,并且存放的都是对象的引用,每次方法的调用都会将方法以及其类信息打包成栈桢存放到虚拟机栈中。所以我们尝试使用递归的方式,不断调用递归方法。
总结,当我们不断调用递归方法时,栈内存设置过小抛出OOM(以后尽量少些递归)。
 
 
 
    
 
posted @ 2019-04-12 00:34  追尾巴的小猪0617  阅读(270)  评论(0)    收藏  举报