jvm和jmm的区别与联系

@[TOC](JVM和JMM区别与联系 转自yolee is zyl.)

一 JMM

  1. JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是java整个计算虚拟模型。

  2. 从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。

  3. 本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。
    在这里插入图片描述

二、JVM对Java内存模型的实现

根据JMM模型,在JVM把内存分成了两部分:线程栈区和堆区。
JVM中运行的每个线程都拥有自己的线程栈(也称调用栈),线程栈包含了当前线程执行的方法调用相关信息。随着代码的不断执行,调用栈会不断变化。
在这里插入图片描述

  1. 线程栈区(Thread stack)
    1)线程栈(thread stack,也叫线程堆栈),所拥有的资源是独自的,对其他线程不可见。
    2)线程栈,也包含正在的所有局部变量。
    3)由当前线程创建的局部变量,对于非创建它的其他所有线程都是不可见的。
    4)即使两个线程正在执行完全相同的代码,两个线程仍然会在每个线程堆栈中创建该代码的局部变量。即便可传递变量副本,但不共享原始局部变量本身。

  2. 堆区(Heap)
    1)堆包含创建的java应用程序对象。
    2)堆中的对象可以被具有对象引用的所有线程访问。当一个线程访问一个对象时,它也可以访问该对象的成员变量。
    3)如果两个线程同时调用同一个对象上的一个方法,它们都可以访问该对象的成员变量,但每个线程都有自己的局部变量副本
    4)堆中的 数据是共享的,线程不安全的

详细说明:

1)所有原始类型(boolean,byte,short,char,int,long,float,double)的局部变 量都直接保存在线程栈当中,对于它们的值各个线程之间都是独立的。对于原始类型的局部变量,一个线程可以传递一个变量副本给另一个线程,但原始变量是不共享的。

2)堆区包含了Java应用创建的所有对象信息(包括原始类型的封装类),不管对象是哪个线程创建的,不管对象是属于一个成员变量还是方法中的局部变量,它都会被存储在堆区。

3)一个局部变量如果是原始类型,那么它会被完全存储到栈区。 一个局部变量也有可能是一个对象的引用,这种情况下,这个本地引用会被存储到栈中,但是对象本身仍然存储在堆区。

4)对于一个对象的成员方法,这些方法中包含局部变量,仍需要存储在栈区,即使它们所属的对象在堆区。

5)对于一个对象的成员变量,不管它是原始类型还是包装类型,都会被存储到堆区。

6)Static类型的变量以及类本身相关信息都会随着类本身存储在堆区。
在这里插入图片描述
基于JMM的JVM模型,既然堆中的数据是共享的,那么在多线程环境中,就可能存在数据安全性问题。主要涉及到:可见性问题,竞争性问题等。

  1. 共享对象的可见性(事例说明)
    如果两个或多个线程共享一个对象,但没有正确使用 volatile 声明或 Synchronized 同步机制,一个线程更新了共享变量值后,对于其他线程来讲是不可见的。如线程A,线程B同时要进行modify
public class Account {

  private float balance;

  public void modify (float difference) {
    float value=this.balance;
    this.balance=value+difference;
  }
}

首先,线程A和线程B在各自的 thread stack中维护了一分局部变量的副本,线程A修改了线程A中Thread stack中的局部变量,但是还没有还没将修改的数据刷新到Main Memory中,而线程B获取的值依然是old value,就会出现问题
解决方案

使用volatile关键字
使用synchronized同步机制

tips:volatile与synchronized的区别:

volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住
volatile仅修饰变量;synchronized则可以修饰变量、方法、代码块
volatile仅保证可见性;synchronized则可以保证可见性和原子性
volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞
volatile修饰的变量会禁止指令重排序,因而程序不会被编译器优化;synchronized修饰的变量没有禁止指令重排序,因而程序可以被编译器优化

三、JVM

程序计数器(PC)
java虚拟机栈
本地方法栈
java堆
方法区

  1. 程序计数器(PC)

是一块很小的内存空间,用于记录将要运行的指令。

tips:每个线程都需要一个程序计数器;各个线程的计数器相互独立,是私有的。

  1. java虚拟机栈

保存了局部变量、部分结果,并参与方法的调用和返回,的一个内存空间

tips:1)对各个线程来说,也是私有的;
2)它和java线程同一时间创建;
3)由java语言实现的

  1. 本地方法栈

与java虚拟机栈的功能相似

tips:1)java虚拟机栈用于管理Java函数的调用,本地方法栈用于管理本地方法的调用;
2)由C语言实现的

  1. java堆

tips:1)做存储的,为所有创建的对象和数组分配内存空间;
2)被JVM中所有的线程共享

  1. 方法区

也被称为永久区,与堆空间相似,被JVM中所有的线程共享。
1)方法区主要保存的信息是类的元数据,方法区中最为重要的是类的类型信息、常量池、域信息、方法信息。其中运行时常量池就在方法区;
2)GC对永久区的回收:一是对永久区常量池的回收;二是永久区对元数据的回收

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值