1-运行时数据区内部结构概述
下面这张图一定要会画
阿里手册中拿到的一张图:
JIT是方法区的一部分,元空间可以理解为是方法区的一个具体实现。
关于线程间共享的说明
API中可以看到:
、
2-线程
JVM系统线程
3-程序计数器(PC寄存器)
1.PC Register介绍
官网:
不存在垃圾回收,不存在OOM。
2.举例说明
1)
2)
3.两个常见问题
4.CPU时间片
4-虚拟机栈
1.虚拟机栈该概述
官方:
虚拟机栈出现的背景
初步印象
内存中的栈与堆
虚拟机栈基本内容
代码:
主要特点
不存在垃圾回收,但是存在OOM。
面试题:开发中遇到的一场有哪些?
设置栈内存大小
官方文档:
2.栈的存储单位
栈中存储什么?
栈运行原理
原理图:
代码解释:
3.栈帧的内部结构
有些地方将方法返回地址、动态链接、一些附加信息归纳为帧数据区。
局部变量表(local variables)
1)字节码中方法内部节后的剖析
查看方式一:
操作数栈的深度为2,局部变量表的深度为3
3个变量为args、test、num
查看方式二:
java代码对应的字节码指令:
局部变量表的大小为3,字节码长度为16
java代码行号对应着字节码指令行号
变量的描述:
1)从上图可以看出11对应16,16行为test.test1();所以15行是声明16行是声明后作用的域。
2)8(初始长度)+8(有效长度)、11+5都等于16,即字节码的长度。
2)关于Slot的理解
解释32bit和64bit:
public String test2(Date dateP, String name2) {
dateP = null;
name2 = "songhongkang";
double weight = 130.5;//占据两个slot
char gender = '男';
return dateP + name2;
}
解释:如果当前帧是由构造器或者实例方法创建的,那么该对象引用this将会存放在index为0的slot处。
//练习:
public static void testStatic(){
LocalVariablesTest test = new LocalVariablesTest();
Date date = new Date();
int count = 10;
System.out.println(count);
// 因为this变量不存在于当前方法的局部变量表中!!
// System.out.println(this.count);
}
//关于Slot的使用的理解
public LocalVariablesTest(){
this.count = 1;
}
public void test1() {
Date date = new Date();
String name1 = "atguigu.com";
test2(date, name1);
System.out.println(date + name1);
}
1)Slot的重复利用
解释Slot重复利用:
public void test4() {
int a = 0;
{
int b = 0;
b = a + 1;
}
//变量c使用之前已经销毁的变量b占据的slot的位置
int c = a + 1;
}
2)静态变量与局部变量的对比
代码:
/*
变量的分类:按照数据类型分:① 基本数据类型 ② 引用数据类型
按照在类中声明的位置分:① 成员变量或域信息(类中全局定义):在使用前,都经历过默认初始化赋值
类变量(static修饰):linking的prepare阶段:给类变量默认赋值 ---> initial阶段:给类变量显式赋值即静态代码块赋值
实例变量(无static修饰):随着对象的创建,会在堆空间中分配实例变量空间,并进行默认赋值
② 局部变量(方法体中):在使用前,必须要进行显式赋值的!否则,编译不通过
*/
public void test5Temp(){
int num;
//System.out.println(num);//错误信息:变量num未进行初始化
}
操作数栈(Operand Stack)
可以使用数组和链表来实现
数组一旦创建,其长度是确定的
代码追踪(字节码指令执行分析)
注意:byte、short、char、boolean都是以int类型来保存
具体解析:
常见面试题 i++和++i 的区别
1)
计算i:
字节码文件:
计算j:
计算k:
2)
public class OperandStackTest {
public void testAddOperation() {
//byte、short、char、boolean:都以int型来保存
byte i = 15;
int j = 8;
int k = i + j;
// int m = 800;
}
public int getSum(){
int m = 10;
int n = 20;
int k = m + n;
return k;
}
public void testGetSum(){
//获取上一个栈桢返回的结果,并保存在操作数栈中
int i = getSum();
int j = 10;
}
/*
程序员面试过程中, 常见的i++和++i 的区别,放到字节码篇章时再介绍。
*/
public void add(){
//第1类问题:
int i1 = 10;
i1++;
int i2 = 10;
++i2;
//第2类问题:
int i3 = 10;
int i4 = i3++;
int i5 = 10;
int i6 = ++i5;
//第3类问题:
int i7 = 10;
i7 = i7++;
int i8 = 10;
i8 = ++i8;
//第4类问题:
int i9 = 10;
int i10 = i9++ + ++i9;
}
}
栈顶缓存(Top-of-Stack Cashing)技术
动态链接(Dynamic Linking)(或指向运行时常量池的方法引用)
代码解释:
public class DynamicLinkingTest {
int num = 10;
public void methodA(){
System.out.println("methodA()....");
}
public void methodB(){
System.out.println("methodB()....");
methodA();
num++;
}
}
#7等就是符号引用
方法的调用
代码解释:
package com.atguigu.java2;
/**
* 说明早期绑定和晚期绑定的例子
* @author shkstart
* @create 2020 上午 11:59
*/
class Animal{
public void eat(){
System.out.println("动物进食");
}
}
interface Huntable{
void hunt();
}
class Dog extends Animal implements Huntable{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void hunt() {
System.out.println("捕食耗子,多管闲事");
}
}
class Cat extends Animal implements Huntable{
public Cat(){
super();//表现为:早期绑定
}
public Cat(String name){
this();//表现为:早期绑定
}
@Override
public void eat() {
super.eat();//表现为:早期绑定
System.out.println("猫吃鱼");
}
@Override
public void hunt() {
System.out.println("捕食耗子,天经地义");
}
}
public class AnimalTest {
public void showAnimal(Animal animal){
animal.eat();//表现为:晚期绑定
}
public void showHunt(Huntable h){
h.hunt();//表现为:晚期绑定
}
}
方法调用:虚方法与非虚方法
子类对象的多态性的使用前提:1.类的继承关系 2.方法的重写
非虚方法不可以重写
代码解释:
package com.atguigu.java2;
/**
* 解析调用中非虚方法、虚方法的测试
*
* invokestatic指令和invokespecial指令调用的方法称为非虚方法
* @author shkstart
* @create 2020 下午 12:07
*/
class Father {
public Father() {
System.out.println("father的构造器");
}
public static void showStatic(String str) {
System.out.println("father " + str);
}
public final void showFinal() {
System.out.println("father show final");
}
public void showCommon() {
System.out.println("father 普通方法");
}
}
public class Son extends Father {
public Son() {
//invokespecial
super();
}
public Son(int age) {
//invokespecial
this();
}
//不是重写的父类的静态方法,因为静态方法不能被重写!
public static void showStatic(String str) {
System.out.println("son " + str);
}
private void showPrivate(String str) {
System.out.println("son private" + str);
}
public void show() {
//invokestatic
showStatic("atguigu.com");
//invokestatic
super.showStatic("good!");
//invokespecial
showPrivate("hello!");
//invokespecial
super.showCommon();
//invokevirtual
showFinal();//因为此方法声明有final,不能被子类重写,所以也认为此方法是非虚方法。
//虚方法如下:
//invokevirtual
showCommon();
info();
MethodInterface in = null;
//invokeinterface
in.methodA();
}
public void info(){
}
public void display(Father f){
f.showCommon();
}
public static void main(String[] args) {
Son so = new Son();
so.show();
}
}
interface MethodInterface{
void methodA();
}
关于invokedynamic指令
代码解释:
package com.atguigu.java2;
/**
* 体会invokedynamic指令
* @author shkstart
* @create 2020 下午 3:09
*/
@FunctionalInterface
interface Func {
public boolean func(String str);
}
public class Lambda {
public void lambda(Func func) {
return;
}
public static void main(String[] args) {
Lambda lambda = new Lambda();
Func func = s -> {
return true;
};
lambda.lambda(func);
lambda.lambda(s -> {
return true;
});
}
}
方法调用:方法重写的本质
方法调用:虚方法表
代码解释:
package com.atguigu.java3;
/**
* 虚方法表的举例
*
* @author shkstart
* @create 2020 下午 1:11
*/
interface Friendly {
void sayHello();
void sayGoodbye();
}
class Dog {
public void sayHello() {
}
public String toString() {
return "Dog";
}
}
class Cat implements Friendly {
public void eat() {
}
public void sayHello() {
}
public void sayGoodbye() {
}
protected void finalize() {
}
public String toString(){
return "Cat";
}
}
class CockerSpaniel extends Dog implements Friendly {
public void sayHello() {
super.sayHello();
}
public void sayGoodbye() {
}
}
public class VirtualMethodTable {
}
方法返回地址(return address)
代码解释:
public class ReturnAddressTest {
public boolean methodBoolean() {
return false;
}
public byte methodByte() {
return 0;
}
public short methodShort() {
return 0;
}
public char methodChar() {
return 'a';
}
public int methodInt() {
return 0;
}
public long methodLong() {
return 0L;
}
public float methodFloat() {
return 0.0f;
}
public double methodDouble() {
return 0.0;
}
public String methodString() {
return null;
}
public Date methodDate() {
return null;
}
public void methodVoid() {
}
static {
int i = 10;
}
}
代码解释:
public class ReturnAddressTest {
public void method2() {
methodVoid();
try {
method1();//72行
} catch (IOException e) {
e.printStackTrace();
}//75行
}
public void method1() throws IOException {
FileReader fis = new FileReader("atguigu.txt");
char[] cBuffer = new char[1024];
int len;
while ((len = fis.read(cBuffer)) != -1) {
String str = new String(cBuffer, 0, len);
System.out.println(str);
}
fis.close();
}
}
一些附加信息
栈的相关面试题
代码解释:
/**
* 面试题:
* 方法中定义的局部变量是否线程安全?具体情况具体分析
*
* 何为线程安全?
* 如果只有一个线程才可以操作此数据,则必是线程安全的。
* 如果有多个线程操作此数据,则此数据是共享数据。如果不考虑同步机制的话,会存在线程安全问题。
* @author shkstart
* @create 2020 下午 7:48
*/
public class StringBuilderTest {
int num = 10;
//s1的声明方式是线程安全的
public static void method1(){
//StringBuilder:线程不安全
StringBuilder s1 = new StringBuilder();
s1.append("a");
s1.append("b");
//...
}
//sBuilder的操作过程:是线程不安全的
public static void method2(StringBuilder sBuilder){
sBuilder.append("a");
sBuilder.append("b");
//...
}
//s1的操作:是线程不安全的
public static StringBuilder method3(){
StringBuilder s1 = new StringBuilder();
s1.append("a");
s1.append("b");
return s1;
}
//s1的操作:是线程安全的
public static String method4(){
StringBuilder s1 = new StringBuilder();
s1.append("a");
s1.append("b");
return s1.toString();
}
public static void main(String[] args) {
StringBuilder s = new StringBuilder();
new Thread(() -> {
s.append("a");
s.append("b");
}).start();
method2(s);
}
}
5-本地方法栈
更多笔记参考网址:https://juejin.im/entry/5e71c5fbe51d4526e651de0b
逃逸分析补充