回来写博客了,还好,没有失言。
先告诉一下读者,也许我也还没能够说的够明白,因为这个问题,真的,还是很复杂的;所以没那份完整的自信。
回归主题。
先来说一下JVM中内存的几个概念。(在上一篇博文中有同学让我加点图,这里特意找来一个class文件结构图,自己画图,总是不会用画笔,哎!)
方法区(Method Area),虚拟机栈(VM Stack),堆(Heap),本地方法栈(Native Method Stack),程序计数器。
那么在这里,就主要介绍一下:VM Stack与Heap。
VM Stack主要存放的是局部变量表部分,而局部变量表存放了编译期可知的各种基本类型数据(boolean,byte,char,short,int,float,long,double),对象引用(reference类型)和returnAddress类型(指向字节码指令的地址)。
Heap主要存放的就是对象实例。
先来用个简单的而复杂的例子说明一下:
Object obj = new Object();
看看这段代码
以上代码是通过javap命令反编译出的class代码,而代码就是:
public class obj {
Object obj = new Object();
}
完整命令:javap -verbose obj。(当然要先通过javac obj.java先编辑一下,再反哦)
再来分析一下上面的代码:
{
java.lang.Object obj;
public obj();
Code:
Stack=3, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: new #2; //class java/lang/Object
8: dup
9: invokespecial #1; //Method java/lang/Object."<init>":()V
12: putfield #3; //Field obj:Ljava/lang/Object;
15: return
LineNumberTable:
line 1: 0
line 12: 4
}
首先:
Stack=3, Locals=1, Args_size=1 其中Stack为栈编号,Locals为本地变量个数,Args_size参数个数。
aload_0:将指定的引用类型本地变量推送至栈顶
invokespecial:调用超类构造方法实例初始化
putfield:为指定的类的实例域赋值
dup:复制栈顶数值并将复制值压入栈顶
new:创建一个对象,并将其引用值压入栈顶
在这面的过程中,首先是在栈中本地变量表中创建一个reference类型数据,再在堆中创建一个Object的实例对象,而对象类型等信息保存到方法区。
再回到标题说的
String a = new String("abc");(当然这样写代码是错误的)
可能大家都知道String做为了一个类实现,那么new一个实例时,就在堆中创建一个实例对象,而“abc”也是保存在这块内存中,与String b = "abc";中的值"abc"存放的地址是不一样的。而a==b比较的是两个地址的值。当然在new String("abc")的时候,abc会放入到共享池中,不过与a引用的地址已经不是一样了。
比如:
package test;
public class String1 {
st a = new st(1);
st b = new st(1);
public static void main(String args[]) {
String1 cfb = new String1();
if(cfb.a == cfb.b){
System.out.println("地址相等,你会相信吗?");
} else{
System.out.println("地址不相等.");
}
}
class st {
int b;
public st(int b){
this.b = b;
}
}
}
最后肯定是输出不相等了。
最后,总结一下上面所说的,还是有些混,没有写的很清楚的感觉,也许这就是我对JVM的理解还不深的原因是,应该把类加载的过程说的更清楚。
最后给一个我看的博文地址:
从“关于Java堆与栈的思考”一帖看错误信息的传播 作者:ZangXT