阿索演讲-演讲技巧,演讲培训,演讲生活 阿索演讲-演讲技巧,演讲培训,演讲生活

黄体酮胶囊,包皮,e网通-阿索演讲-演讲技巧,演讲培训,演讲生活

一、运转时数据区域

  • 相应脑图

程序计数器

记载正在履行的虚拟机字节码指令的地址(假如正在履行的是本地办法则为空)。

Java 虚拟机栈

每个 Java 办法在履行的同时会创立一个栈帧用于存储局部变量表操作数栈常量池引证等信息。 从办法调用直至履行完结的进程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的进程。 关于履行引擎来说,活动线程中,只要栈顶的栈帧是有用的,称为当时栈帧,这个栈帧所相关的办法称为当时办法。 履行引擎所运转的一切字节码指令都只针对当时栈帧进行操作。

操作数栈:
一个后进先出(Last-In-First-Out)的操作数栈,也能够称之为表达式栈(Expression Stack)。
操作数栈和局部变量表在拜访办法上存在着较大差异,操作数栈并非选用拜访索引的办法来进行数据拜访的,
而是**经过规范的入栈和出栈操作来完结一次数据拜访**。
每一个操作数栈都会具有一个清晰的栈深度用于存储数值,一个32bit的数值能够用一个单位的栈深度来存储,而2个单位的栈深度则能够保存一个64bit的数值,
当然操作数栈所需的容量巨细在编译期就能够被彻底确认下来,并保存在办法的Code特点中。

能够经过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存巨细:

java -Xss512M HackTheJava

该区域或许抛出以下反常:

  • 当线程恳求的栈深度超越最大值,会抛出 StackOverflowError 反常;
  • 栈进行动态扩展时假如无法申请到满足内存,会抛出 OutOfMemoryError 反常。

本地办法栈

本地办法栈与 Java 虚拟机栈相似,它们之间的差异只不过是本地办法栈为本地办法服务。

本地办法一般是用其它言语(C、C++ 或汇编言语等)编写的,而且被编译为依据本机硬件和操作体系的程序,对待这些办法需求特别处理。

一切方针都在这儿分配内存,是废物搜集的首要区域("GC 堆")。

现代的废物搜集器根本都是选用分代搜集算法,其首要的思维是针对不同类型的方针采纳不同的废物收回算法,能够将堆分红两块:

  • 重生代(Young Generation)
  • 老时代(Old Generation)

堆不需求接连内存,而且能够动态添加其内存,添加失利会抛出 OutOfMemoryError 反常。

能够经过 -Xms 和 -Xmx 两个虚拟机参数来指定一个程序的堆内存巨细,第一个参数设置初始值,第二个参数设置最大值。

java -Xms1M -Xmx2M HackTheJava

办法区

用于寄存已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

和堆相同不需求接连的内存,而且能够动态扩展,动态扩展失利相同会抛出 OutOfMemoryError 反常。

对这块区域进行废物收回的首要方针是对常量池的收回和对类的卸载,可是一般比较难完结。

HotSpot 虚拟机把它当成永久代来进行废物收回。可是很难确认永久代的巨细,由于它遭到许多要素影响,而且每次 Full GC 之后永久代的巨细都会改动,所以经常会抛出 OutOfMemoryError 反常。为了更简单办理办法区,从 JDK 1.8 开端,移除永久代,并把办法区移至元空间,它坐落本地内存中,而不是虚拟机内存中。

运转时常量池

运转时常量池是办法区的一部分。

Class 文件中的常量池(编译器生成的各种字面量和符号引证)会在类加载后被放入这个区域。

除了在编译期生成的常量,还答应动态生成,例如 String 类的 intern()。

直接内存

在 JDK 1.4 中新加入了 NIO 类,它能够运用 Native 函数库直接分配堆外内存(Native 堆),然后经过一个存储在 Java 堆里的 DirectByteBuffer 方针作为这块内存的引证进行操作。

这样能在一些场景中明显进步功用,由于防止了在 Java 堆和 Native 堆中来回仿制数据

二、HotSpot虚拟机方针

方针的创立

方针的创立进程:

  1. 类加载查看

虚拟机遇到一条 new 指令时,首要将去查看这个指令的参数是否能在常量池中定位到这个类的符号引证, 而且查看这个符号引证代表的类是否已被加载过、解析和初始化过。 假如没有,那有必要先履行相应的类加载进程。

  1. 分配内存

在类加载查看经往后,接下来虚拟机将为重生方针分配内存。 方针所需的内存巨细在类加载完结后便可确认,为方针分配空间的使命等同于把一块确认巨细的内存从 Java 堆中区分出来。分配办法有 “指针磕碰” 和 “闲暇列表” 两种,挑选那种分配办法由 Java 堆是否规整决议, 而Java堆是否规整又由所选用的废物搜集器是否带有紧缩收拾功用决议。

  • 内存分配的两种办法

  • 内存分配并发问题

在创立方针的时分有一个很重要的问题,便是线程安全,由于在实践开发进程中,创立方针是很频频的作业, 作为虚拟机来说,必需求确保线程是安全的,一般来讲,虚拟机选用两种办法来确保线程安全:

(1)CAS+失利重试: CAS 是达观锁的一种完结办法。所谓达观锁便是, 每次不加锁而是假定没有抵触而去完结某项操作, 假如由于抵触失利就重试,直到成功停止。 虚拟机选用CAS配上失利重试的办法确保更新操作的原子性。

(2)TLAB: 每一个线程预先在Java堆中分配一块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。 哪个线程要分配内存,就在哪个线程的TLAB上分配,只要TLAB用完并分配新的TLAB时,才选用上述的CAS进行内存分配。

  1. 初始化零值

内存分配完结后,虚拟机需求将分配到的内存空间都初始化为零值(不包括方针头), 这一步操作确保了方针的实例字段在 Java 代码中能够不赋初始值就直接运用, 程序能拜访到这些字段的数据类型所对应的零值。

  1. 设置方针头

初始化零值完结之后,虚拟机要对方针进行必要的设置, 例如这个方针是那个类的实例、怎么才干找到类的元数据信息、方针的哈希吗、方针的 GC 分代年纪等信息。 这些信息寄存在方针头中。 别的,依据虚拟机当时运转状况的不同,如是否启用倾向锁等,方针头会有不同的设置办法。

  1. 履行init办法

在上面作业都完结之后,从虚拟机的视角来看,一个新的方针现已发生了, 但从 Java 程序的视角来看,方针创立才刚开端, 办法还没有履行,一切的字段都还为零。 所以一般来说,履行 new 指令之后会接着履行 办法, 把方针依照程序员的志愿进行初始化,这样一个真实可用的方针才算彻底发生出来。

方针的内存布局

在 Hotspot 虚拟机中,方针在内存中的布局能够分为3块区域:

(1)方针头

(2)实例数据

(3)对齐填充

  1. 方针头

Hotspot虚拟机的方针头包括两部分信息:

一部分用于存储方针自身的运转时数据(哈希码、GC分代年纪、锁状况标志等等),

另一部分是类型指针,即方针指向它的类元数据的指针,虚拟机经过这个指针来确认这个方针是那个类的实例

  1. 实例数据

实例数据部分是方针真实存储的有用信息,也是在程序中所界说的各种类型的字段内容。

  1. 对齐填充

对齐填充部分不是必定存在的,也没有什么特别的意义,只是起占位效果。 由于Hotspot虚拟机的主动内存办理体系要求方针开始地址有必要是8字节的整数倍, 换句话说便是方针的巨细有必要是8字节的整数倍。而方针头部分正好是8字节的倍数(1倍或2倍), 因而,当方针实例数据部分没有对齐时,就需求经过对齐填充来补全。

方针的拜访定位

树立方针便是为了运用方针,咱们的Java程序经过栈上的 reference 数据来操作堆上的详细方针。 方针的拜访办法视虚拟机的完结而定,现在干流的拜访办法有两种:

(1)运用句柄

(2)直接指针

  1. 运用句柄

假如运用句柄的话,那么Java堆中将会区分出一块内存来作为句柄池, reference中存储的便是方针的句柄地址,而句柄中包括了方针实例数据与类型数据各自的详细地址信息

  1. 直接指针

假如运用直接指针拜访,那么Java 堆方针的布局中就有必要考虑怎么放置拜访类型数据的相关信息, 而reference中存储的直接便是方针的地址

这两种方针拜访办法各有优势:

(1)运用句柄来拜访的最大优点是 reference 中存储的是安稳的句柄地址, 在方针被移动时只会改动句柄中的实例数据指针,而reference自身不需求修正

(2)运用直接指针拜访办法最大的优点便是速度快,它节省了一次指针定位的时刻开支。

三、String类和常量池

  1. String方针的两种创立办法
String str1 = "abcd";
String str2 = new String("abcd");
System.out.println(str1==str2);//false

这两种不同的创立办法是有不同的:

第一种办法是在常量池中获取方针("abcd" 归于字符串字面量,因而编译时期会在常量池中创立一个字符串方针),

第二种办法总共会创立两个字符串方针(条件是 String Pool 中还没有 "abcd" 字符串方针)。

  • "abcd" 归于字符串字面量,因而编译时期会在常量池中创立一个字符串方针,指向这个 "abcd" 字符串字面量;
  • 运用 new 的办法会在堆中创立一个字符串方针。

  1. String类型的常量池比较特别。它的首要运用办法有两种:
  • 直接运用双引号声明出来的String方针会直接存储在常量池中。
  • 假如不是用双引号声明的String方针,能够运用 String 供给的 intern 办法。 String.intern() 是一个 Native 办法,它的效果是: 假如运转时常量池中现已包括一个等于此 String 方针内容的字符串,则回来常量池中该字符串的引证; 假如没有,则在常量池中创立与此 String 内容相同的字符串,并回来常量池中创立的字符串的引证
String s1 = new String("计算机");
String s2 = s1.intern();
String s3 = "计算机";
System.out.println(s2);//计算机
System.out.println(s1 == s2);//false,由于一个是堆内存中的String方针一个是常量池中的String方针,
System.out.println(s2 == s3);//true,由于两个都是常量池中的String方针
  1. 字符串拼接:
String str1 = "str";
String str2 = "ing";

String str3 = "str" + "ing";//常量池中的方针
String str4 = str1 + str2; //TODO:在堆上创立的新的方针
String str5 = "string";//常量池中的方针
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//true
System.out.println(str4 == str5);//false

留意:尽量防止多个字符串拼接,由于这样会从头创立方针。 假如需求改动字符串的话,能够运用 StringBuilder 或许 StringBuffer

面试题:String s1 = new String("abc");问创立了几个方针?

创立2个字符串方针(条件是 String Pool 中还没有 "abcd" 字符串方针)。

  • "abc" 归于字符串字面量,因而编译时期会在常量池中创立一个字符串方针,指向这个 "abcd" 字符串字面量;
  • 运用 new 的办法会在堆中创立一个字符串方针。

(字符串常量"abc"在编译期就现已确认放入常量池,而 Java 堆上的"abc"是在运转期初始化阶段才确认)。

String s1 = new String("abc");// 堆内存的地址值
String s2 = "abc";
System.out.println(s1 == s2);// 输出false
//由于一个是堆内存,一个是常量池的内存,故两者是不同的。
System.out.println(s1.equals(s2));// 输出true

四、8种根本类型的包装类和常量池

  • Java根本类型的包装类的大部分都完结了常量池技能, 即Byte,Short,Integer,Long,Character,Boolean; 这5种包装类默许创立了数值**[-128,127]**的相应类型的缓存数据, 可是超出此规模依然会去创立新的方针。
  • 两种浮点数类型的包装类Float,Double 并没有完结常量池技能

valueOf() 办法的完结比较简单,便是先判别值是否在缓存池中,假如在的话就直接回来缓存池的内容。

Integer的部分源码:

public static Integer valueOf(int i) {

if (i >= IntegerCache.low && i <= IntegerCache.high)

return IntegerCache.cache[i + (-IntegerCache.low)];

return new Integer(i);

}

在 Java 8 中,Integer 缓存池的巨细默许为 -128~127。、

  • 示例1:
Integer i1=40;
//Java 在编译的时分会直接将代码封装成 Integer i1=Integer.valueOf(40);然后运用常量池中的方针。
Integer i2 = new Integer(40);
//创立新的方针。
System.out.println(i1==i2);//输出false
  • 示例2:Integer有主动拆装箱功用
Integer i1 = 40;
Integer i2 = 40;
Integer i3 = 0;
Integer i4 = new Integer(40);
Integer i5 = new Integer(40);
Integer i6 = new Integer(0);

System.out.println("i1=i2 " + (i1 == i2)); //输出 i1=i2 true
System.out.println("i1=i2+i3 " + (i1 == i2 + i3)); //输出 i1=i2+i3 true
//i2+i3得到40,比较的是数值
System.out.println("i1=i4 " + (i1 == i4)); //输出 i1=i4 false
System.out.println("i4=i5 " + (i4 == i5)); //输出 i4=i5 false
//i5+i6得到40,比较的是数值
System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); //输出 i4=i5+i6 true
System.out.println("40=i5+i6 " + (40 == i5 + i6)); //输出 40=i5+i6 true

原文:https://my.oschina.net/u/3728792/blog/3047080

作者:admin 分类:趣闻中心 浏览:267 评论:0