Java内存数据模型
本篇文章带来的是对Java内存数据模型的介绍,这对于我们深入理解Jvm虚拟机工作的原理和Java内存的划分大有裨益,好了,为了让我们理解的更为深刻,我们将会加入图片辅助的方法去理解。
本篇博文的目录:
一:Java内存数据模型的介绍
二:线程私有内存
三:程序计数器
四:Java虚拟机栈
五:本地方法栈
六:Java堆
七:方法区
八:运行时常量池
九:总结
一:java内存数据模型的介绍
java程序在运行的时候会在内存中开辟不同的空间用以管理不用的内存区域,每个区域都有自己的功能,创建和销毁时间,有的区域会随着虚拟机的启动而创建,而有的区域会随着用户线程的创建而销毁。按照空间,分为如下不同的空间,接下来我们将按照不同的区域进行学习,学习它不同区域的功能和用法
图 1-1 :java内存数据模型
二:线程私有内存
我们先来看一下多线程运行原理:其实cpu在运行的时候每次在同一特定的时间点只能运行一个线程(单核的情况下),只不过它切换的速度非常之快,让我们觉的它是在进行多线程运行,本质上它还是单一运行的。所以这就会必然引出一个问题:cpu切换线程如何保证它自身的运行不受其他的线程影响,保证每个线程都是独立不受外部侵扰的,这就产生了线程私有内存这个概念,主要是维持线程的安全、稳定、高效的运转。关于这一点也很好理解,比如我们去一个快速餐厅吃饭,我们点好了菜,会产生菜单小票,这个小票上面有编号,就是每个线程的“私有内存”,服务员再进行不同顾客上菜的时候就有了区分度,这样就可以顺利找到不同的顾客(切换不同的线程);在java线程中,程序计数器、虚拟机栈、本地方法栈都属于线程私有的。
注意点:线程私有不存在多线程并发的资源竞争问题,因为其享有的内存是互不影响的,不存在并发问题
与线程私有相对,就有线程公有内存,这个区域在jvm虚拟机中有个特定的称呼叫做:主内存
三:程序计数器:
对应于图上标记的深红色部分,主要是指程序在运行过程中所执行的字节码(.class)文件的行号指示,也称作行号指示器,程序流程的分支、循环、异常处理等基础功能都需要它的引导来完成。它占有的内存空间比较小,它的运行的原理是:通过改变计数器的值来选取下一条的需要执行的字节码命令,而这个值具体指的是虚拟机在Java方法中字节码指令的地址,但是如果执行的是Native方法,那么它的计数器的值是为空的。
注意:
1:它是java中唯一一个没有内存溢出(out of memory)情况的区域。可以思考一下这点是为什么?它是进行线程运行的指示灯,如果没有了它,程序也就无法运行了。
2:它属于线程私有的:每个线程内部都有一个程序计数器,为了保证每个线程切换前后都能正常运行。
四:Java虚拟机栈:
虚拟机栈主要是作用在java方法运行时候,每个线程在运行到一个方法的入口都会创建一个栈帧,创建栈帧的目的在于存储局部变量、操作数栈、动态链接、方法出口等信息。按照这个原理,那么一个方法在从调用直到完全执行完毕,都会对应一个从入栈到出栈的过程。
这里我们说明一下局部变量,它的含义就是定义在java方法内部中的变量,对应于java中8大基本数据类型,byte、int、float、boolean、char、short、long、reference:(long和double占用2个空间,其余1个)局部变量表里我们需要注意的是,它在编译期已经完全了内存分配,这样栈帧在用局部变量的时候,它占用的内存是已经确认的,不需要再分配,这就一定程度上减少了栈帧的工作量。
注意:
1:当线程请求的栈帧的深度大于虚拟机允许的深度,就会抛出stackOverFlow异常
2:我们平时所说的堆栈,其中的栈就是指的是这里的栈帧
3:它也是线程私有的,并且和线程的生命周期相同。
图 2-1:虚拟机栈
五:本地方法栈:
本地方法栈,顾名思义它代表的就是本地方法Native执行的栈帧,在jdk的源码中我们可以看到很多命名为Native的方法,Native方法很大一部分是采用C/C++写的,但是它对Native采用的数据结构、语言等都没有做具体的要求,这一点完全是由java虚拟机进行实现的。
注意:
1:这个区域会抛出oom或者sof(stackoverflowError)异常
2:同样它属于本地线程私有的
六:Java堆:
Java堆主要是存储对象的地方,同时它也是java内存管理区域最大的一块。当我们在程序中new一个对象出来或者新建一个数组,就会把对象存储这个区域。它是java的最重要的公共区域之一,与线程私有相对,它是属于线程公有的。基本上所有的对象都会存储在此区域,但也不是绝对的(随着JIT技术的成熟,这一点会发生微变)。堆是主要存放对象的地方,同时它就会产生另一个问题就是GC(Garbage Collection),当垃圾进行回收的时候,虚拟机并没有开辟新的空间,还在此区域进行,同时这个区域也叫“GC堆”,再细致划分下去,分为新生代、老年代,同时还分为Eden空间、From survivor空间、To Survior区域。为什么要划分的这么细致呢?其实是为了更加有效率的回收,划分的区域越细致,那么垃圾回收器收集的时候只要去对应的地方直接回收就行,不用加上额外的判断逻辑。
注意点:
1:堆在逻辑上是连续不间断的内存空间,但是在物理上可以是不连续的内存空间
2:在堆中如果没有完成内存的实例分配就会抛出oom
3:java堆是线程公有的,所有的线程共享这一片区域
图 3-1 java堆内存
七:方法区
方法区主要是用来存放已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,比如我们在代码中定义的Constant常量就会在这个区域存储。在java虚拟机规范中,它是属于堆的逻辑部分。同时在这个区域中,它也会有垃圾回收器工作,这个区域叫做“永久代”,之所以叫做永久代,因为它比新生代和老年代拥有更长的生命周期,但是并不是在这个区域它就会万事大吉了,永久代依然会存在垃圾回收的情况,只不过相对来说较少
注意点:
1:此区域属于线程公有的,线程的类信息、常量、静态变量、编译代码都在此区域进行存储
2:此区域会抛出oom异常,发生在方法区无法进行内存分配时
图:4-1 方法区
八:运行时常量池:
运行时常量池主要是方法区的一部分,class文件除了有类的版本、字段、方法接口等描述信息外,还有一项信息是常量池,用于存储编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区运行时常量池中存放。
注意:此区域同样会发生oom异常
九:总结
本篇博文主要是对java的内存数据模型区域划分进行了简单的介绍,没有深入细致过多的介绍。不过我们在整体上有一个感性的认识,理解java内存区域的划分,以及这样的划分的好处,还有设计到线程的部分,理解并发的问题。这部分属于虚拟机底层的东西,属于进阶。相信看完这篇文章后,我希望能回答以下几个问题。同时这些东西也是考究我们对于底层的认识,面试中也是经常会被提及到的东西
1:java内存模型分为哪几个区域?
2:请说出属于线程私有部分和线程公有部分的区域?
3:我们都知道内存是在按照“代”进行划分的,那么请问java堆中内存分为哪几代?分别位于什么区域?
4:为什么要把java内存划分这么多,划分这么多不嫌麻烦吗?有什么好处
我希望能思考以上问题,学习的时候就要进行多思考,深入思考这是为什么?这篇博文的介绍就到此结束了,下一篇见,对于java内存模型的理解有助于我们处理并发的问题,这是属于javaEE中高端进阶必不可少的基础,在此留个记录,同时分享出来,让大家也能清楚的认识Java内存模型同时提醒自己深入理解。
主要参考资料:
《深入Jvm虚拟机》
Java内存数据模型的更多相关文章
- Java内存溢出
中间件应用Java内存溢出常见的三种情况: 1.OutOfMemoryError: Java heap space 2.OutOfMemoryError: PermGen space 3.OutOfM ...
- JVM学习(3)——总结Java内存模型
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 为什么学习Java的内存模式 缓存一致性问题 什么是内存模型 JMM(Java Memory Model)简 ...
- 浅析java内存模型--JMM(Java Memory Model)
在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步? 在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的. 线程之间通过共享程序公共的状态,通 ...
- JMM(java内存模型)
What is a memory model, anyway? In multiprocessorsystems, processors generally have one or more laye ...
- 《深入理解Java内存模型》读书总结
概要 文章是<深入理解Java内容模型>读书笔记,该书总共包括了3部分的知识. 第1部分,基本概念 包括"并发.同步.主内存.本地内存.重排序.内存屏障.happens befo ...
- java内存泄漏的几种情况
转载于http://blog.csdn.net/wtt945482445/article/details/52483944 Java 内存分配策略 Java 程序运行时的内存分配策略有三种,分别是静态 ...
- java内存区域简介
运行时数据区域 1.程序计数器:是一块较小的内存空间,可以看做当前线程所执行的字节码的行号指示器.字节码解释器工作时就是通过改变计数器的值来选取下一条需要执行的字节码指令,分支.循环.跳转.异常处理. ...
- cache4j轻量级java内存缓存框架,实现FIFO、LRU、TwoQueues缓存模型
简介 cache4j是一款轻量级java内存缓存框架,实现FIFO.LRU.TwoQueues缓存模型,使用非常方便. cache4j为java开发者提供一种更加轻便的内存缓存方案,杀鸡焉用EhCac ...
- Java内存模型深度解析:final--转
原文地址:http://www.codeceo.com/article/java-memory-6.html 与前面介绍的锁和Volatile相比较,对final域的读和写更像是普通的变量访问.对于f ...
随机推荐
- 基于51单片机IIC通信的PCF8591学习笔记
引言 PCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入.一个输出和一个串行I2C 总线接口.3 个地址引脚A0.A1 和A2 用于编程硬件地址,允许将最多8 个器件连接 ...
- struts2 之 Action的优化配置
总结:struts2种action的配置文件会随着业务的增加而增加,导致配置文件膨胀.struts2中提供了三种方案来解决这个问题: 1. 动态方法调用来实现. 2. 通配符配置来解决. 3. 使用注 ...
- iOS 关于定位你该注意的那些事
其实现在对于一个APP来说,定位用户的位置是件很容易的事情,有三种解决方案供您选择: (1)原生 (2)高德地图 (3)百度地图 1.解决方案的选择 其实单说iOS开发来说应用哪种方案都无所谓 ...
- Go 并发随机打印1-n
package main import ( "fmt" "math/rand" "sync" "t ...
- JS中的几种函数
函数可以说是js中最具特色的地方,在这里我将分享一下有关函数的相关知识: 包装函数: (function foo(){...})作为函数表达式意味着foo只能在...所代表的位置中被访问 ...
- NI Vision for LabVIEW 基础(一):NI Vision 简介
NI Vision 控件模板 Vision控件模板位于LabVIEW控件模板的最顶层,由一下元素组成: IMAQ Image.ctl—该控件是一个类型定义,用于声明图象类型的数据.在VI的前面板中使用 ...
- Block Token 原理分析
介绍 文件权限检查由NameNode执行,而不是DataNode执行. 默认情况下,任何客户端都可以访问只有其块ID的任何块. 为了解决这个问题,Hadoop引入了块访问令牌的概念. 块访问令牌由Na ...
- JavaScript中apply与call方法
一.定义 apply:应用某一对象的一个方法,用另一个对象替换当前对象. call:调用一个对象的一个方法,以另一个对象替换当前对象. 二.apply //apply function Person( ...
- 思考题:用Use Case获取需求的方法是否有什么缺陷,还有什么地方需要改进?(提示:是否对所有的应用领域都适用?使用的方便性?.......)
思考题: 用Use Case获取需求的方法是否有什么缺陷,还有什么地方需要改进?(提示:是否对所有的应用领域都适用?使用的方便性?.......) 简答: 一.用例解释: 在软件工程中,用例是一种在开 ...
- Vuejs实例-01使用vue-cli脚手架搭建Vue.js项目
[TOC] 1. 前言 vue-cli 一个简单的构建Vue.js项目的命令行界面 整体过程: $ npm install -g vue-cli $ vue init webpack vue-admi ...