Java高级之内存模型分析
博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved !
下文是博主感悟,请带着怀疑性的态度阅读!
需要了解基本变量所占内存大小,请移步:读书笔记-类结构的认识
Java存储空间有这么几块-来源于Java编程思想
寄存器:位于处理器内部,不受外层代码控制,由处理器自行分配-C/C++可以建议分配方式
堆栈:位于RAM中 引用和基本数据类型存放的区块。 指针向下生成新对象,向上释放对象(new关键字),相当于链表结构。
堆:位于RAM中 对象存放的区块
常量存储:位于ROM中 存放于方法体中
非RAM存储:流对象和持久化数据-存储到硬盘
说到存储就难免讲到JVM的垃圾回收机制,需要了解的同学可以点进去看看
如果要实现处理器的高效率,那么就要压榨它的每一寸(byte)的运行能力,I3的处理器达到3.4GHz,即每秒运算3.4亿次,因此给它划分任务块,每块分配足够多的任务,实现高并发;所以对内存的模型需要详细了解。
由于硬件的读写速度与处理器的运算速度差距过大,一般都会写一层高速缓存来作为缓冲,一边从硬盘读数据到缓存,一边把处理器的处理结果写入缓存,一边把缓存中要写入的数据写到硬盘;因此很多程序会使用到中间件。
如果多个处理器同时处理缓存,就需要拟定协议谁先谁后,对于同一个处理器中的任务也是同样如此,有sychronzied关键字来处理;同时处理器还会对一段程序丧心病狂的进行(OOOE)乱序处理,也就是顺序在前面的代码并不一定先执行,对于依赖前段程序结果的代码来说,就需要通过其他途径来保证顺序性。
内存模型定义的关键在于第一使各处理器的操作不具有歧义 第二不影响拓展各自的特性;它主要定义虚拟机存取数据的细节,定义所有变量都存储在主内存,每条线程都有自己的工作内存(主内存的副本,或者叫引用),不同线程的工作内存互不直接访问,通过主内存来影响各自对值的引用;拿虚拟机来做例子,寄存器、栈、堆缓存就像工作内存,硬件设备就是主内存。
定义了八种操作来完成上述存取过程
lock和unlock 作用于主内存,标识为某线程独占或释放,成对存在
read和load 读取和加载,从主内存将数据读给工作内存,再加载到工作内存,成对存在
use和assign 使用和赋值 作用于工作内存,将变量给工作引擎,将接收到的值进行处理 成对存在
store和write 存储和写入 从工作内存将数据存回主内存,再写入主内存 成对存在
顺序过程unlock放到write后面即可。不允许读不入工作内存,也不允许写不入主内存;新变量只能在主内存中产生,不能跳级执行,lock与unlock一样重复执行多次,只是每次lock工作内存则被清空。lock可类比为Java的Lock对象。
讲完上面的存取过程,变量的原子性就很好讲了,原子性指对变量的存取过程顺序执行,要么执行完,要么不执行,不允许其他线程对其进行污染。而带有特殊含义的sychronzied和final关键字,就可以用原子性来解释:前者由于保障了unlock之前变量已同步到主内存,这里的变量指方法体或类中所有的;后者是避免构造器把this引用传递出去,因而像惰性气体一样稳定。
另外java的先行发生原则,很有意思,有以下几种表现形式
1、程序控制流顺序执行,即代码顺序执行
2、volitale和锁顺序执行,即前一个锁执行结束,后一个得到锁
3、Thread的start方法先于run方法内的方法执行
4、通过isAlive、interrupt和join方法判断线程是否存活
5、对象结束先于finilize方法执行
6、A先于B,B先于C,可得出A先于C执行的传递性。
最后再讲下volatile关键字,它有两个作用
1、保证改变后马上通知其他线程(执行write操作后,变量马上刷新),即对其他线程的可见性
2、保障上面所指丧心病狂的处理器处理此变量不被乱序操作,即禁止指令重排优化
但是volatile没有原子性(PS:原子性指read-assign-store这3组,只要一个执行,就会全部执行),不能保证作为计数器而正确存在;所以一般如果很少对它标识的变量进行改变的场景比较适用,比如多条线程共同执行多个有父类的任务,一个条件通知结束,则所有线程一起结束;就像劳动节来临,不论工程师还是设计师,都可以休息一天。
补充一点,64位的long和double无原子性,会被当成两个32位变量来处理,但一般默认为具有原子性,占用两个局部变量的位置
虚拟机运行时的数据区域有以下几种
虚拟机栈 主要存放引用和基本数据类型
堆 主要存放对象
方法区 常见的类信息除对象以外的所有,包括类信息(数据类型),常量池,方法、接口、静态变量等
本地方法栈 用来执行native方法
程序计数器 存储下一条需要执行的字节码指令,每条线程都有一个
虚拟机的多线程是通过线程切换并分配执行时间,同时一个内核在任一时刻只处理一条线程的指令
虚拟机栈和堆是线程共享的数据区,方法区、本地方法栈和程序计数器是线程所不能访问到的数据区
其中数据访问的方式有两种:一种是句柄形式,引用指向句柄,句柄包含对象地址和对象类型;一种是指针,直接存储对象地址,以句柄少一步,所以访问也会快一些,而HotSpot就是用这种;前者也有一定优化,值发生改变时,引用不用变,后者要改变指针才行。
内存异常有两种表现,一种叫OutOfMemoryError(内存溢出),请求的虚拟机扩展栈已无足够空间,分配给新对象,典型的标记-清理算法容易产品这种情况,另一种叫StackOverflowError(内存泄露),请求的栈深度超过虚拟机所允许 ,例如下标超过数据大小,一般线程不同步会引起这种状况的产生。
接下来对中英文分别占多少字节进行解释
public static void main(String[] args) {
String[] charsetNames = { "utf-8", "utf-16", "UTF-16BE", "UTF-16LE", "UTF-32", "UTF-32BE", "UTF-32LE", "unicode", "GBK", "GB2312", "GB18030",
"ISO8859-1", "BIG5", "ASCII" };
for (int i = 0; i < charsetNames.length; i++) {
printByteLength(charsetNames[i]);
}
}
/**
public static void printByteLength(String charsetName) {
* String类的不带参数的getBytes()方法会以程序所运行平台的默认编码方式为准来进行转换,
* 在不同环境下可能会有不同的结果,因此建议使用指定编码方式的getBytes(String charsetName)方法。
*/
String a = "a"; // 一个英文字符
String b = "啊"; // 一个中文字符
try {
System.out.println();
System.out.println(charsetName + "编码英文字符所占字节数:" + a.getBytes(charsetName).length);
System.out.println(charsetName + "编码中文字符所占字节数:" + b.getBytes(charsetName).length);
} catch (UnsupportedEncodingException e) {
System.out.println("非法编码格式!");
}
}
utf-8编码英文字符所占字节数:1
utf-8编码中文字符所占字节数:3 utf-16编码英文字符所占字节数:4
utf-16编码中文字符所占字节数:4 UTF-16BE编码英文字符所占字节数:2
UTF-16BE编码中文字符所占字节数:2 UTF-16LE编码英文字符所占字节数:2
UTF-16LE编码中文字符所占字节数:2 UTF-32编码英文字符所占字节数:4
UTF-32编码中文字符所占字节数:4 UTF-32BE编码英文字符所占字节数:4
UTF-32BE编码中文字符所占字节数:4 UTF-32LE编码英文字符所占字节数:4
UTF-32LE编码中文字符所占字节数:4 unicode编码英文字符所占字节数:4
unicode编码中文字符所占字节数:4 GBK编码英文字符所占字节数:1
GBK编码中文字符所占字节数:2 GB2312编码英文字符所占字节数:1
GB2312编码中文字符所占字节数:2 GB18030编码英文字符所占字节数:1
GB18030编码中文字符所占字节数:2 ISO8859-1编码英文字符所占字节数:1
ISO8859-1编码中文字符所占字节数:1 BIG5编码英文字符所占字节数:1
BIG5编码中文字符所占字节数:2 ASCII编码英文字符所占字节数:1
ASCII编码中文字符所占字节数:1
Linux默认可以存放100个进程,放1个跟99个是一样的,其他均为sleep状态,意思是已经开辟这么大内存,用还是不用,反正都在那里放着,不会浪费CPU时间,因此后台进程只要不是说一直处于活动状态,跟IOS一样,无需杀死后台进程。
String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2); // 字符串的话是可以直接相等,跟int等其他数据类型一样
System.out.println(str1.equals(str2)); // 字符串的话是可以直接相等,跟int等其他数据类型一样
String strObj1 = new String("hello");
String strObj2 = new String("hello");
System.out.println(strObj1 == strObj2);// 值不等
System.out.println(strObj1.equals(strObj2));// 对象相等,因为char全相等
执行结果:
true
true
false
true
分析:拿String作为例子,其他容器类同样类似
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
首先判断是否是同一个对象,即两个引用指向同一个对象,那自然相等;
其次比较是否是同一种类型,如不等则返回false,如相等则继续;
最后比较内部的值,String的char,List的value,Map的key和value,如果完全相等则相等。
Java高级之内存模型分析的更多相关文章
- Java内存模型分析
在学习Java内存模型之前,先了解一下线程通信机制. 1.线程通信机制 在并发编程中,线程之间相互交换信息就是线程通信.目前有两种机制:内存共享与消息传递. 1.1.共享内存 Java采用的就是共享内 ...
- 关于JAVA中的static方法、并发问题以及JAVA运行时内存模型
一.前言 最近在工作上用到了一个静态方法,跟同事交流的时候,被一个问题给问倒了,只怪基础不扎实... 问题大致是这样的,“在多线程环境下,静态方法中的局部变量会不会被其它线程给污染掉?”: 我当时的想 ...
- Java对象的内存模型(一)
前言 新人一枚,刚刚入门编程不久,各方面都在学习当中,博文有什么错误的地方,希望我们可以多多交流! 最近,在开发App后台过程中,需要将项目部署到云服务器上.而云服务器的内存大小却只有1G.要如何做到 ...
- Java虚拟机:内存模型详解
版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 我们都知道,当虚拟机执行Java代码的时候,首先要把字节码文件加载到内存,那么这些类的信息都存放在内存中的哪个区域呢?当我们创建一个对象实 ...
- java虚拟机的内存模型
一.为什么要了解java虚拟机的内存模型 java虚拟机作为java代码运行的平台,是java技术的基石.了解java虚拟机的内存模型也就变得十分必要.它能帮助我们更好的了解java代码的运行机制,更 ...
- java内存模型分析2
不同线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要在主内存来完成,线程.主内存和工作内存的交互关系如下图所示,和上图很类似. 这里的主内存.工作内存与Java内存区域的Java堆. ...
- Java虚拟机—Java8内存模型(整理版)
1.概述 对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要手动释放内存,不容易出现内存泄露和内存溢出问题.一旦出现内存泄露和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,排查错误 ...
- 深入理解JAVA虚拟机(内存模型+GC算法+JVM调优)
目录 1.Java虚拟机内存模型 1.1 程序计数器 1.2 Java虚拟机栈 局部变量 1.3 本地方法栈 1.4 Java堆 1.5 方法区(永久区.元空间) 附图 2.JVM内存分配参数 2.1 ...
- 深度解析Java多线程的内存模型
内部java内存模型 硬件层面的内存模型 Java内存模型和硬件内存模型的联系 共享对象的可见性 资源竞速 Java内存模型很好的说明了JVM是如何在内存里工作的,JVM可以理解为java执行的一个操 ...
随机推荐
- MD5算法 简介
MD5(单向散列算法)的全称是Message-Digest Algorithm 5(信息-摘要算法),经MD2.MD3和MD4发展而来.MD5算法的使用不需要支付任何版权费用. MD5功能 l 输入任 ...
- BZOJ4060 : [Cerc2012]Word equations
首先通过hash建树 设f[i][j]表示第i个特殊符号从P的第j位开始匹配能到达哪里 记忆化搜索,对于底层贪心匹配. #include<cstdio> #include<cstri ...
- HTML5离线应用无法更新的定位与解决
一.些许前提 最近在制作一个Web应用, 其中用到了HTML5的离线应用功能(offline application), 离线应用的概念就不再阐述, 可以查看这两篇文章: http://www.ibm ...
- 【wikioi】1553 互斥的数(hash+set)
http://wikioi.com/problem/1553/ 一开始我也知道用set来判a[i]/p是否在集合中,在的话就直接删掉. 但是我没有想到要排序,也没有想到当存在a,b使得a/p==b时到 ...
- OpenCV count the number of connected camera 检测连接的摄像头的数量
有时候在项目中我们需要检测当前连接在机子上的摄像头的数量,可以通过下面的代码实现,其中连接摄像头的最大数量maxCamNum可以任意修改: /** * Count current camera num ...
- fibonacci数列 java
public class Fibonacci { public static void main(String agrs[]) { ;j<=;j++) System.out.println(fo ...
- react-native 学习
官网 https://facebook.github.io/react-native/docs/getting-started.html#content 下载 npm install -g react ...
- 本函数用来改变目前 php 执行的目录到新的 directory 目录中
chdir : 改变目录. dir : 目录类别类. closedir : 关闭目录 handle. opendir : 打开目录 handle. readdir : 读取目录 handle. rew ...
- 为什么java里用常量赋值就相等,用字符串就不等?
例一: String s0="HF"; String s1=new String("HF"); System.out.println(s0==s1); 输入为什 ...
- 持续集成篇_05_SonarQube代码质量管理平台的介绍与安装
1.SonarQube的介绍 SonarQube是一个管理代码质量的开放平台. 可以从七个维度检测代码质量(为什么要用SonarQube): (1)复杂度分布(complexity):代码复杂度过高将 ...