【JVM基础】JVM内存模型

JVM内存模型

在这里插入图片描述

程序计数器(寄存器)

  • 作用 :记住下一条jvm指令的执行地址
  • 特点: 是线程私有的,不会存在内存溢出

栈是线程运行需要的内存空间,栈帧是每个方法运行时需要的内存,栈帧内存放:

  • 局部变量表。数字数组,用于存放方法参数和方法体内局部变量。参数值的存放总是在局部变量数组的index0开始,到数组长度-1的索引结束
  • 返回地址
  • 表达式栈
  • 动态链接(或指向运行时常量池的方法引用)
  • 一些附加信息

虚拟机栈

  • 和本地方法栈相似,区别在于java虚拟栈为虚拟机执行Java方法(也就是字节码)服务,本地方法栈则是为虚拟机使用到的本地方法服务
  • 每个线程运行所需要的内存称为虚拟机栈
  • 每个线程只能有一个活动栈帧,对应着正在执行的那个方法

方法区

  • 方法区存放类型信息、常量、静态变量、即时编译器编译后的代码缓存
  • 方法区内存线程共享。方法区在JVM启动时创建,关闭JVM释放内存。实际物理内存和Java堆一样都可以是不连续的
  • 方法区的大小可动态扩展。方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误: java. lang .OutofMemoryError:
    PermGen space 或者java. lang.OutOfMemoryError:Metaspace

  • JVM只有一个堆区,堆用来存放对象和数组,不存放基本类型和对象引用只存放对象本身。堆内存线程共享
  • 堆可以动态分配内存大小。堆分为堆内存和非堆内存,堆内存分为年轻代和老年代,非堆内存是永久代。年轻代又分为Eden和Survivor区,Survivor区由FromSpace和ToSpace组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
  • 为融合HotSpot JVM与JRockit VM(新JVM技术)JDK1.8将永久代替换为元空间,因为JRockit没有永久代。元空间是使用本地内存,元空间有两个参数:
    • MetaspaceSize :初始化元空间大小,控制发生GC阈值
    • MaxMetaspaceSize : 限制元空间大小上限,防止异常占用过多物理内存

参数 描述
-Xms 堆内存初始大小,单位m、g
-Xmx(MaxHeapSize) 堆内存最大允许大小,一般不要大于物理内存的80%
-XX:PermSize 非堆内存初始大小,一般应用设置初始化200m,最大1024m就够了
-XX:MaxPermSize 非堆内存最大允许大小
-XX:NewSize(-Xns) 年轻代内存初始大小
-XX:MaxNewSize(-Xmn) 年轻代内存最大允许大小,也可以缩写
-XX:SurvivorRatio=8 年轻代中Eden区与Survivor区的容量比例值,默认为8,即8:1
-Xss 堆栈内存大小

GC

算法:
- 引用计数器。给对象中添加一个计数器,引用对象则+1引用失效-1,任何时刻计数器为0的对象就是不可能在被使用。
- 复制算法。FROM空间和TO空间大小一致,当FROM空间被完全占满时,GC将活动对象复制到TO空间,然后再清除FROM空间所有的对象,这里就包括了FROM空间中活动的对象和不活动的对象,完毕后将TO空间和FROM空间对换。
- 优点:分配过程向堆的一端压缩解决了内存碎片化的问题。
- 缺点:一半空间(from)使用一半空间空着(to),浪费内存空间
- 标记清除法。扫描:对活动对象进行标记。清除:对没有标记的对象进行清除。
- 优点:不需要额外的空间,
- 缺点:两次扫描,浪费时间开销,会产生内存碎片
- 标记压缩法。(可设置清除次数在压缩)扫描:对活着的对象进行标记。清除:对没有标记的对象进行清除。压缩:防止内存碎片产生,再次扫描,将存活的对象向前移动,使对象内存连续,防止内存碎片产生,多一个移动成本。
- 优点:不需要额外的空间,防止内存碎片
- 缺点:多次扫描,浪费时间开销

总结

  • 内存效率:复制算法>标记清除法>标记压缩法
  • 内存整齐度:复制算法=标记压缩算法>标记清除算法
  • 内存利用率:标记压缩算法=标记清除算法>复制算法
    新生代:存活率低,复制算法
    老年代: 区域大存活率高,标记清除+标记压缩混合实现

双亲委派模型

类加载器包含:
启动类加载器:负责加载Java_HOME目录下,lib的目录下的类库。
扩展类加载器:负责加载Java_HOME目录下,lib子目录下的ext的子目录的类库
应用程序类加载器:加载用户路径classpath上的类库。
自定义类加载器:通过继承 java.lang.ClassLoader,根据不同的需求来实现自定义的类加载器。

  • 向上委派:一个类在收到类加载请求后,不会自己加载这个类,而是把这个类加载请求向上委派给它的父类去完成,父类收到这个请求后又继续向上委派给自己的父类,以此类推,直到所有的请求委派到启动类加载器中。
  • 向下委派:当父类加载器在接收到类加载请求后,发现自己也无法加载这个类(这个情况通常是因为这个类的Class文件在父类的加载路径中不存在)这时父类会把这个信息反馈给子类,并向下委派子类加载器来加载这个类,直到这个请求被成功加载,但是一直到自定义加载器都没有找到,JVM就会抛出ClassNotFund异常。
  • 为什么需要双亲委派? 保证类的安全性和唯一性,避免重复加载
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值