JVM内存模型

JVM内存模型

首先上图
在这里插入图片描述

主要概念

堆、栈、本地方法栈、方法区、程序计数器、直接内存、常量池

Java堆是程序员需要重点关注的一块区域,因为涉及到内存的分配(new关键字,反射等)与回收(回收算法,收集器等);

对于大多数应用来说,Java 堆(Java Heap)是Java 虚拟机所管理的内存中最大的一块。

  • new出来的对象都存在堆里面

  • 所有线程共享

  • Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可

  • 其他地方的对象语言都是存放地址来指向此处

堆分为年轻代(1/3)、老年代(2/3);年轻代又分为Eden(4/5)区和Survior(1/5)区;Survior区被一分为二,From Survivor 空间、To Survivor 空间,用于GC的复制。

线程私有,生命周期和线程,每个方法在执行的同时都会创建一个 栈帧用于 存储局部变量表,操作数栈,动态链接,方法出口等信息。方法的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程;栈里面存放着各种基本数据类型和对象的引用;

  • 栈帧,一个线程被划分成许多的栈帧,每一个方法都是一个栈帧(如main函数栈帧、run函数栈帧)

  • 局部变量表,方法内部的int a=0;User user= new User();这种局部变量(或者地址引用)会被保存在这个地方。(其实远没有这么简单,准确来说,javac编译后的class文件,会将这些变量定义转换成另外一种存储,简单来理解是正确的)

  • 操作数栈,顾名思义,就是一种栈的数据结构类型,当程序运行时,会将代码中用到的变量存入到这个栈中,如int a = b+c;会先将b和c入栈(先从局部变量表中取出),然后调用加法(自动出栈两个元素相加),结果入栈,在局部变量表定义一个新变量a存放出栈的结果

  • 动态链接, 被调用的目标方法在编译期无法被确定下来,只能够在程序运行期将方法的符号引用转换为直接引用,这种引用转换的过程具备动态性,称为动态链接。方法的绑定机制分为早期绑定(Early Binding)晚期绑定(Late Bingind)。绑定是一个字段、方法或类在符号引用被替换为直接引用的过程。早期绑定: 被调用的目标方法在编译期可知,且运行保持不变。晚期绑定: 被调用方法在编译期无法被确定下来,只能够在程序运行期根据实际类型绑定相关的方法。

  • 静态链接:当一个字节码文件被装载进JVM内部时,如果被调用的****目标方法在编译期可知,且运行期保持不变。将调用方法的符号引用转换为直接引用的过程称为静态链接

  • 举个例子,静态方法就是使用静态链接,而多态机制,即动态调用子类的方法就是动态机制。除了被static修饰的静态方法,被final修饰的禁止子类覆盖的方法(invokevirtual),所有类的初始化方法(invokespecial),所有被private修饰的私有方法(invokespecial) ,jvm采用静态绑定机制来调用这些方法。

  • 方法出口:指示这个方法运行完了会返回至哪一个地方

本地方法栈

线程私有,本地方法栈保存的是native方法的信息,当一个JVM创建的线程调用native方法后,JVM不再为其在虚拟机栈中创建栈帧, JVM只是简单地动态链接并直接调用native方法;(很多java的底层静态方法都是调用C语言写的方法)

方法区

线程共享,也叫永久区(本质上两者并不等价),用于存储已经被虚拟机加载的类信息,常量(“zdy”,"123"等),静态变量(static变量)等数据。 (jdk1.8已经将方法区去掉了,将方法区移动到直接内存)。用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

JDK1.8直接 将方法区去掉,在本地内存中新增 元数据空间。运行时常量池仍然在堆中。元数据区存放类加载信息。

运行时常量池

运行时常量池是方法区的一部分,用于存放编译期生成的各种字面(“zdy”,"123"等)和符号引用。

jDK1.7将运行时常量池从方法区移除到堆内存(也就是局部变量表)。

直接内存

不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域;

1)如果使用了NIO(New Input/Output)(引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用Native 函数库直接分配堆外内存),这块区域会被频繁使用,在java堆内可以用directByteBuffer对象直接引用并操作;

2) 这块内存不受java堆大小限制,但受本机总内存的限制,可以通过MaxDirectMemorySize来设置(默认与堆内存最大值一样),所以也会出现OOM异常;

程序计数器

线程私有,较小的内存空间, 当前线程执行的字节码的行号指示器;各线程之间独立存储,互不影响;

当线程被挂起(失去时间片、被其他线程抢占cpu时),为了避免从头开始运行方法,就使用程序计数器来恢复到之前运行到的地方

线程私有和线程共享

多线程共享内存区域: 方法区、堆。

每一个线程独享内存: java栈、本地方法栈、程序计数器。
在这里插入图片描述

线程安全

在这里插入图片描述

线程安全本质是由于多个线程对同一个堆内存中的Count变量操作的时候,每一个线程会在线程内部创建这个堆内存Count变量的副本,线程内所有的操作都是对这个Count副本进行操作。这时如果其他线程操作这个堆内存Count变量,改变了Count值对这个线程是不可见的。当前线程操作完Count变量将值从副本空间写到主内存(堆内存)的时候就会覆盖其他线程操作Count变量的结果,引发线程不安全问题。

GC流程

GC Roots根节点:线程栈的本地变量、静态变量、本地方法栈的变量等等

从GC Roots根节点开始往下找它所引用的,这些就是存活的变量,不会被清理,其他都会被GC回收(每次GC都会停掉其他线程,专门开一个线程来执行GC)

  • 首先第一次 From Survivor 空间、To Survivor 空间都为空,此时Eden区满了,触发GC(minor gc),寻找GC Roots根节点能达到的对象,将其他对象清除调,剩余的对象放置From Survivor空间。此时第一次GC完成后,Eden:0,From Survivor:存活的对象,To Survivor:0。
  • 每经历一次GC活下来的对象,对象头中的分代年龄都会+1
  • Eden又满了,又触发GC(minor gc),此时由于一个 Survivor空间(From Survivor空间)有对象存活,所以会在Eden区和From Survivor空间中进行GC,将活下来的对象全部移动到To Survivor 空间。此时第二次GC完成后,Eden:0,From Survivor:0,To Survivor:存活的对象。
  • 分代年龄都会+1
  • 重复上述过程(From Survivor空间和To Survivor相互交换)(复制算法)
  • 当对象的分代年龄都达到15时,这时GC就会认为这是个“老不死”的对象,就会将其移动到老年代(常见的比如spring的各种bean)
  • 当老年代也放满了,触发触发GC(full gc)(清理堆中所有的。也会清理方法区)

jvisualvm:查看jvm的内存情况。

jVM常数配置

-Xms 初始堆内存大小

-Xmx 最大堆内存大小

-Xss 单个线程栈大小

-XX:NewSize 初始新生代堆大小

-XX:MaxNewSize 生代最大堆大小

-XX:PermSize 方法区初始大小(JDK1.7及以前)

-XX:MaxPermSize 方法区最大大小(JDK1.7及以前)

-XX:MetaspaceSize 元数据区初始值(JDK1.8)

-XX:MaxMetaspaceSize 元数据区最大值(JDK1.8)

参数设置示例

jdk1.7 windows设置tomcat的catalina.bat

set JAVA_OPTS=-Xms1024m -Xmx1024m -Xss1m -XX:PermSize=128m -XX:MaxPermSize=256m -XX:NewSize=256m -XX:MaxNewSize=256m

jdk1.8 windows设置tomcat的catalina.bat

set JAVA_OPTS=-Xms1024m -Xmx1024m -Xss1m -XX:MetaspaceSize=128m -XX:MAXMetaspaceSize=256m -XX:NewSize=256m -XX:MaxNewSize=256m

jdk1.7 linux设置tomcat的catalina.sh

JAVA_OPTS=-Xms1024m -Xmx1024m -Xss1m -XX:PermSize=128m -XX:MaxPermSize=256m -XX:NewSize=256m -XX:MaxNewSize=256m

jdk1.8 linux设置tomcat的catalina.sh

JAVA_OPTS=-Xms1024m -Xmx1024m -Xss1m -XX:MetaspaceSize=128m -XX:MAXMetaspaceSize=256m -XX:NewSize=256m -XX:MaxNewSize=256m

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值