JVM主要包括三块内存空间,分别是栈内存、堆内存和方法区
Java虚拟机包含类装载器子系统、执行引擎、运行时数据区、本地方法接口和垃圾收集模块。
类装载器子系统:根据给定的全限定类名(如: java.lang.Object)来装载class文件到运行时数据区域的方法区中。
执行引擎:执行字节码或执行本地方法。
运行时数据区:就是常说的JVM的内存,堆,方法区,虚拟机栈,本地方法栈,程序计数器。
本地方法接口:与本地方法库交互,作用就是为了融合不同编程语言为Java所用,它的初衷是融合C/C++程序
首先通过编译器把Java代码转换成字节码,类加载器再把字节码加载到内存中(运行时数据区的方法区内),而字节码文件只是JVM的一套指令集规范,不能直接交给底层系统去执行,所以需要特定的命令解析器执行引擎将字节码翻译成底层系统指令,再交给CPI去执行,而这个过程需要调用其他语言的本地库接口来实现整个程序的功能。
方法的代码片以及整个类的代码片断被存储到方法区内存中,在类加载的时候这些代码片段会被载入。方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
方法执行使用的是栈,该方法需要的内存空间在栈内存中分配,称为压栈。方法执行结束后,该方法所占用的内存空间自动释放,称为弹栈。栈中主要存储的是方法体当中的局部变量。
引用也叫句柄,类似于指针,但是和指针是不同的。指针是一个存放地址的变量,使程序员可以灵活的访问内存,由于可以对指针进行任意的运算操作,所以给程序带来了安全隐患和意想不到的结果。引用继承了指针节省内存的优点,但是限制了对地址的操作,它是安全的。Java中所有的变量都是一个引用,java中没有指针的概念。
Person p=new Person();--调用的是Person类中的无参构造器 class Person{ private String name; private Job[] jobs; }
在程序执行过程中使用new运算符创建的java对象,存储在堆内存中。对象内部有实例变量,所以实例变量存储在堆内存当中。
局部变量,方法体中声明
成员变量,方法体外类内声明
三块主要内存中变化最频繁的是栈内存,最先有数据的是方法区内存,垃圾回收期主要针对的是堆内存
问:自动垃圾回收机制GC什么时候会将java对象的内存回收?
答:当堆内存中的对象称为垃圾数据的时候会被GC回收
问:什么时候堆内存对象会变成垃圾?
答:1、引用计数法:为每个对象创建一个引用计数器,有对象引用时计数器+1,引用被释放时计数器-1,当计数器为0时就可以被回收。它有一个缺点就是不能解决循环引用的问题。
2、可达性算法(引用链法)︰从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是可以被回收的。
栈内存存储基本类型的变量和对象的引用变量
堆内存用于存放由new创建的对象和数组。每new一个对象就在堆内存中开辟一个新的存储空间存储此实例对象
Person p = new Person()执行new命令时程序执行两步:a:在堆内存中开辟一段空间,存储new出来的对象;b:在栈内存中添加一个变量p,p中存放的是该对象在堆内存中开始存放处的物理地址
p = null;执行此步骤的时候程序只是更改栈内存中的p变量所保存的地址,把地址指向null,而并没有操作堆内存(把p所指向的对象实例清空回收)
无论是形参或者实参,执行XXX = null;操作时都是把XXX变量栈中存储的地址改为指向null的地址。不操作堆中的数据。
public void pp(int k) { System.out.println(k); } pp(1);
问:由类创建一个对象,JVM内存中发生了哪些事情?
答:MyClass mc = new MyClass();
以这条语句为例, MyClass mc =使虚拟机栈中生成了一个指向MyClass对象的地址;new MyClass()则在堆中分配了对象mc成员变量的空间。
无参无返(没有参数列表,没有返回值)单纯的作为功能代码的聚合使用便于功能复用
无参有返(没有参数列表,有返回值)
有参无返(有参数列表没有返回值)适用于功能需要根据参数来进行计算的情况,但是计算的最终结果又无需返回处理
有参有返(有参数列表,有返回值)适用于功能需要根据参数来进行计算的情况,而且最终的结果需要返回处理
形参:是定义在方法声明上,用于指定该方法需要传递的参数类型的
实参:是调用方法时,实际传递的参数值
在不可变类Integer类定义中查看源代码可以发现一个定义
private static class Integercache {} //这实际上就是Integer的cache
Integer num1 = 12; Integer num2 = 12; System.out.println(num1 == num2);//这块相等,<=127都是真的
是因为在Integer中包含有一个缓存池,缓存值为-128到127之间。
Integer k1 = 12; Integer k2 = 12; System.out.println(k1 == k2); //true,k1和k2都是引用类型,所以==比较的是地址 Integer k3=new Integer(12); System.out.println(k1==k3) ; //false Integer k4 = 129; Integer k5 = 129; System.out.println(k4 == k5); //fa1se,因为缓存只缓存-128到127之间的数据
Integer\Long\Short\Byte中都有缓存池、charactcr中也有缓存池;boolean只有true、falsc两个
String中包含一个缓存池,当使用某个字符串对象时会首先在缓存池中进行查找,如果存在则直接返回这个对象的地址;如果不存在则会在缓存池中进行创建,创建完成后返回地址。
String s1="abc " ; String s2="abc "; String s3=new String("abc"); //这里会创建2个对象,一个在缓存池中,一个是new导致的新创建对象 System.out.println(s1==s2) ; //true System.out.println(s1==s3); //false
注意:如果通过字串拼接所得内容和某个字串内容一致,但是地址不同
语法:数据类型…变量名。必须作为最后一个参数出现
具体处理过程中实际上是按照数组的方式进行处理, 而且数组不会为null
public void aa(int...k) { if(k.length>O〕{ for(int i = 0;i<k.length;i++) System .out.print1n(k[i]); } }
void pp(Object… arr)可变长参数可以是Object[ ] 数组
注意: 一个方法的最后一个位置只有一个, 所以方法中的可变个数的参数只能有一个
值类型自动转换成它对应的类类型—autoboxing
类类型自动转成它所对应的值类型—unboxing
Integer a = 9;
int c = new Integer(9);