1. JVM , JRE和JDK
JVM:加载class文件并进行解析
JRE(java running environment):JVM+API (java核心类库)
JDK:JRE+工具(包括javacjavadoc等工具)
2. java内存区
包括方法区、java堆、java栈、PC寄存器、本地方法栈。
方法区(或者叫永久代permanent generation):包括类信息、方法信息(如一些private修饰之类的)、常量池、静态变量和class loader的引用。常量池指的是在编译期被确定,并被保存在.class文件中的一些数据。所以如果可以从常量池中取就不要去new一个。如Integeri=Integer.valueOf(10)会比Integer i=new Integer(10)好。这部分数据是所有线程共享的。
java堆:存放对象实例,所有的实例和数组都是在堆上分配的。Java堆和方法区一样是被所有线程共享的。Java堆有更细致的划分:新生代(采用minor gc)、老龄代(采用full gc),无论对java堆如何划分都是为了更好的回收内存,或更快的分配内存。根据VM Spec的要求,java 堆可以处于内存上不连续的空间,只要逻辑上连续即可。如果在堆中无法分配内存将会报OutOfMemeryError。
Java栈:存了很多栈帧。一个栈帧对应了java中的方法。栈帧包括局部变量、操作数栈(对数据的计算操作)、栈数据区(存放异常信息和解析静态变量--与方法区沟通)。每个方法被执行的时候都会创建一个栈帧,一个方法的调用至完成对应了栈帧的入栈至出栈过程。默认的栈大小是128K,可以手动配置。如果线程请求(比如线程栈1中的age是10,线程栈2的age是20)的栈深度大于栈大小,将抛出StackOverflowError。
PC寄存器:每个java线程都有一个PC寄存器用于保存程序执行到哪一个指令,对于非native的方法,这个区域记录的是正在执行的地址。
本地方法栈:为本地使用的方法服务,与java栈作用相同。会先去找本地方法接口,再通过本地方法接口调用本地方法,这里就体现了JVM的跨平台,本地方法接口可以有不同的实现。
3. 结合JVM内存,解释为什么在java中,说String是不可变的。
比如String s =new String(“wo”);String s1 = new String(“de”);
s = s + s1;
首先系统在堆上new出一个数组来存放“wo”,在栈中有个变量s指向堆中的“wo”对象,栈中的s1指向“de”对象;当执行s=s+s1时,系统重新在堆中new出一个更大的数组对象,然后将“wo”和“de”都复制进去,然后栈中的s指向这个新的对象。所谓的不可变是指:它没有在原数组“wo”上进行修改,而是新建了个更大数组进行扩展,也就是说,这时候堆里还是有“wo”这个对象数组存在的,只不过这个时候"s"变量不在指向"wo"这个数组了,而是指向了新new出来的数组,这就是和StringBuffer的区别,后者是在原数组上进行修改,改变了原数组的值,StringBuffer不是通过新new一个数组去复制,而是在原数组基础上进行扩展...再让变量指向原数组。