栈是连续的空间,先进后出,从技术上说,栈就是CPU寄存器里的某个指针所指向的一片内存区域。这里所说的“某个指针”通常位于x86/x64平台的ESP寄存器/RSP寄存器,以及ARM平台的SP寄存器。
栈会给每个线程都分配一个栈帧,java中8大基本类型和对象的引用都是在栈内存中分配,在栈里同名值是共享内存的,比如:
int a=3;
int b=3;//a,b指向的是同一个"3",但是和对象的引用不同,改变a的值并不会影响b
a=4;//使a=4,如果栈里面没有"4",那就会重新创一个"4"
对象的引用一旦调用了new关键字,他就会在堆内存开辟一个存储空间。
Person person;//这个person就是对象的引用
person = new Person();//这里用new关键字,在堆里面给实例对象分配一块内存
栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。
压栈和弹栈:就是入栈和出栈
之前看过一篇文章总结的很好,下面附上超链接:栈帧
1.Java中所有对象的存储空间都是在堆中分配的,简单来说就是:
引用在栈,实例在堆,下面用一个常见的例子来说明
String str = "haha";
String str1 = "haha";
String str2 = new String("haha");
String str3 = new String("haha");
str == str1;//true
str == str2;//false
str2 == str3;//false
上面的str和str1没有使用new创建对象,"haha"属于常量,在常量池,共用一个“haha”,同一块空间
str2和str3都用了new关键字,开辟了两块空间,所以是不同的对象。
2.一个JVM只有一个堆内存,堆内存的大小是可以调节的,堆存储的全是对象
而对象在内存中的存储布局分为三部分:
A.对象头
java里面用两个字来表示对象头,一个是Mark Word,一个是Klass pointer(Class MetaData Address)
(1)Mark Word(包含了自身运行时数据)
包括锁状态(lock,bkased_lock等),hashcode,GC分代年龄(age),线程持有的锁等… …
(2)Klass pointer(就是一个指针)
虚拟机通过这个指针来确定对象是哪个类的实例
64位虚拟机的对象组成(有锁无锁):
B.实例数据
就是你在类里面写的东西
C.对齐填充
JVM要求对象起始地址必须是8字节的整数倍(8字节对齐),所以不够8字节就由这部分来补充。
3.堆内存分为三个区域:
新生代(发生Minor GC)新生代采取淘汰机制,活不下去的就被轻GC回收
? (1)伊甸园:
梦开始的地方,这个区域人满了,大家就要进行Minor GC,活下来的去幸存区
(2)幸存区:
幸存区分为两块,from区和to区。那为什么要分为两块呢?
因为在新生代会发生 Minor GC(轻度垃圾回收),15次回收还没有淘汰的会进入老年代,进行 Full GC(重度垃圾回收),而Full GC花费的时间和资源是远大于Minor GC的,因此我们将幸存区分为两块,在from和to之间用 四大算法 中的复制来进行筛选机制。
新创建的对象一般会被分配到伊甸区,经过一次Minor GC后,如果对象还存活,就会被移到Survivor区。from区的对象如果继续存活,且能够被另一块幸存区to区容纳,则使用复制算法将这些仍然存活的的对象复制到另一块幸存区to区中,然后清理使用过的Eden和from区(下一次分配就从to区开始,to区成为下一次GC的from区),且这些对象的年龄设置为1,以后对象在幸存区每经历一次Minor GC,对象的年龄就会+1,当对象的年龄到达某个阈值的时候,这些对象就会进入老年代。(阈值默认是15,可以通过-XX:MaxTenuringThreshhold来设定对象在新生代在存活的次数)
ABC经过一次Minor GC还存活进入幸存from区,下次Minor GC幸存者被复制到to区
然后清空from区,将to区变成新的from区,继续筛选,存活15次的进入老年代。
为什么要进行十五次筛选呢?
因为对象头采用四个字节来存放GC的信息:二进制1111刚好对应十进制的15.
老年代(发生Full GC)
标记清除算法:标记要回收的对象统一回收。
和标记整理算法:标记要回收的对象,向同一端移动,移出边界的对象。
永久代(1.8后改为元空间)
此区域常驻内存,用来存放JDK自身携带的Class对象,Interface元数据,此区域不存在垃圾回收,关闭JVM时永久代自动关闭
1.6之前:永久代,常量池在5.方法区
1.7:永久代,常量池在堆中
1.8:无永久代
Java刚开始的时候底层是用c实现的,后面逐渐演变为新语言Java后,仍然保留了一些c里面的方法,而本地方法区就是用来存这些方法的。这些方法的使用需要Native关键字
java中的程序计数器是一块比较小的内存空间,它是当前线程字节码执行的行号指示器。
和堆一样,线程共享,为了和堆区分,也称为非堆