程序计数器:
程序计数器会保存下一条指令的地址!!!
如果是64位操作系统,也就是有64根地址线,那么地址对应的大小就是8个字节
本地方法栈:
存储一些用C++语言编写的 native方法
虚拟机栈:
函数入栈形成栈帧,函数执行完出栈,销毁栈帧。
每个线程都有自己的方法栈。
栈中的数据不会被垃圾回收
灵魂四问❓
栈帧包括什么:
局部变量表 - 保存this和变量的。注意局部变量是可以复用内存槽的,int占1个槽位 long占2个槽位
栈内存会被JVM回收嘛:
不会,方法调用完出栈
栈内存分配的越大越好吗:
不是,因为栈内存大了,线程数就少了,并发少了,只是你单线程能调用的方法多了一点,并不让你运行速度变快,反而会慢。
方法内的局部变量是线程安全的吗?
是的,因为每一个线程的栈帧是私有的,不会共享那个变量,如果变量加了static那就要考虑线程安全问题了。还有就是变量在参数或者返回值也都面临线程安全问题。
方法区:
1.8及以前:逻辑上是堆的一部分,它是所有线程共享的一块内存区域,他存储了类的成员变量、构造方法、类的结构、运行时常量池等。
1.8以后:从堆改为元空间,用的是操作系统的内存,但是这个运行时常量池还是在堆中。
区分串池和常量池
常量池:存放Class类的信息
串池:也叫运行时常量池,程序运行时,会将程序中第一次用到的常量存放到串池中,串池中维护的是一个HashTable,key就是字符串的地址,值就是字符串的内容。
故:
我们new出来的对象是在堆上的,地址不同,所以
String s1 = "ab";
String s2 = new String("ab");
s1 != s2;
像"a" + "b“这种在编译时编译器就优化了,从串池中找有无”ab"。
下面的代码很重要
String s = new String("a") + new String("b"); System.out.println(s.hashCode()); String s2 = s.intern(); // 有返回串池中的对象,没有将s的引用放进去 System.out.println(s.hashCode()); String x = "ab"; System.out.println(x.hashCode()); System.out.println(s2.hashCode());
intern()方法应用实例
场景还原:流传推特要存储用户名和用户地址,但是这需要30个G的内存空间,并且呢,这些用户许多地址都是重复的,那么使用了intern之后,内存直接从30G变成了几百MB。
intern原理:
将字符串放到Stringtable中,Stringtable是内存中维护的一个Hashtable,也就是那个运行时常量池,它是由数组+链表/红黑树构成的,不允许重复,存储时根据hashcode算出的地址值,存到相应的位置。
intern方法:
如果StringTable中存在该字符串,直接返回字该字符串的引用,如果不存在,将该字符串的引用放入到StringTable中。
堆:
new 出来的对象就是在堆中存储的
堆中的对象会被JVM垃圾回收器回收。
直接内存(操作系统中的):
如果Java要使用操作系统中的内存,需要创建两处缓存,一处是系统缓存区,一处是Java缓存区,这样就影响性能,如果使用bytebuffer中的某些方法创建的缓存,可以直接被我们Java程序使用。有待补充~