javac : 编译代码(即将.java文件编译成.class的中立字节码文件) eg:javac App.java
javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。
-help --help -? 输出此用法消息 -c 对代码进行反汇编 -constants 显示静态最终常量 -l 输出行号和本地变量表 -v -verbose 输出附加信息(包括行号、本地变量表,反汇编等详细信息) -version 版本信息,其实是当前javap所在jdk的版本信息,不是class在哪个jdk下生成的。
java源代码:
package myproject; public class App { public static void main( String[] args ) { App app=new App(); int result=app.count(4); System.out.println(result); } public int count(int a){ int b=3; int c=a+b; return c; } }
在App.java目录中执行javac App.java 进行编译,生成App.class
然后执行: javap -v App生成如下内容
警告: 二进制文件App包含myproject.App Classfile /D:/project/Sample/src/main/java/myproject/App.class Last modified 2019-9-8; size 492 bytes MD5 checksum ab5ebd975fe3cab41580ef51c90d8389 Compiled from "App.java" public class myproject.App SourceFile: "App.java" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: //常量池 #1 = Methodref #7.#18 // java/lang/Object."<init>":()V #2 = Class #19 // myproject/App #3 = Methodref #2.#18 // myproject/App."<init>":()V #4 = Methodref #2.#20 // myproject/App.count:(I)I #5 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream; #6 = Methodref #23.#24 // java/io/PrintStream.println:(I)V #7 = Class #25 // java/lang/Object #8 = Utf8 <init> #9 = Utf8 ()V #10 = Utf8 Code #11 = Utf8 LineNumberTable #12 = Utf8 main #13 = Utf8 ([Ljava/lang/String;)V #14 = Utf8 count #15 = Utf8 (I)I #16 = Utf8 SourceFile #17 = Utf8 App.java #18 = NameAndType #8:#9 // "<init>":()V #19 = Utf8 myproject/App #20 = NameAndType #14:#15 // count:(I)I #21 = Class #26 // java/lang/System #22 = NameAndType #27:#28 // out:Ljava/io/PrintStream; #23 = Class #29 // java/io/PrintStream #24 = NameAndType #30:#31 // println:(I)V #25 = Utf8 java/lang/Object #26 = Utf8 java/lang/System #27 = Utf8 out #28 = Utf8 Ljava/io/PrintStream; #29 = Utf8 java/io/PrintStream #30 = Utf8 println #31 = Utf8 (I)V { public myproject.App(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 7: 0 public static void main(java.lang.String[]);//【main方法】 flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: new #2 // class myproject/App 3: dup //[复制上一步创建的对象的引用压入栈] 4: invokespecial #3 // Method "<init>":()V 【调用当前的init方法】 7: astore_1 8: aload_1 9: iconst_4 10: invokevirtual #4 // Method count:(I)I 【调用count方法】 13: istore_2 14: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 17: iload_2 18: invokevirtual #6 // Method java/io/PrintStream.println:(I)V 21: return LineNumberTable: line 11: 0 line 12: 8 line 13: 14 line 14: 21 public int count(int); flags: ACC_PUBLIC Code: stack=2, locals=4, args_size=2 0: iconst_3 1: istore_2 2: iload_1 3: iload_2 4: iadd 5: istore_3 6: iload_3 7: ireturn LineNumberTable: line 17: 0 line 18: 2 line 19: 6 }
常量池(Constant pool):实际上分为两种形态:静态常量池和运行时常量池
静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。这种常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:
-
- 类和接口的全限定名
- 字段名称和描述符
- 方法名称和描述符
运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
方法区:JVM执行时候会将class加载到方法区,在JDK1.8中方法区已经不存在,原方法区中存储的类信息、编译后的代码数据等已经移动到了元空间。对于创建的对象会分配到内存堆中;其中方法区和内存堆都是线程共享;
程序计数器:在操作系统调用线程时候,每一个线程都包含一个程序计数器用来保存当前线程执行的位置,方便下次继续往下执行;
栈:当一个新的线程创建时,JVM会为这个线程创建一个新的Stack。一个Java Stack在一个个独立的栈帧中存储了线程的状态。
栈帧:当一个线程执行一个Java方法时,JVM将创建一个新的栈帧并且把它push到栈顶。此时新的栈帧就变成了当前栈帧,方法执行时,使用栈帧来存储参数、局部变量、中间指令以及其他数据。
堆:堆内存主要用于存放对象和数组,它是JVM管理的内存中最大的一块区域,堆内存和方法区都被所有线程共享,在虚拟机启动时创建。
在垃圾收集的层面上来看,由于现在收集器基本上都采用分代收集算法,因此堆还可以分为新生代(YoungGeneration)和老年代(OldGeneration),新生代还可以分为Eden、From Survivor、To Survivor。
volatile
1、可见性 修饰的变量如果是对象或数组之类的,其含义是对象获数组的地址具有可见性,但是数组或对象内部的成员改变不具备可见性:
2、顺序性
3、不具备原子性
参考:JVM知识点精华汇总
查看内存对象工具:jol