jvm堆内存的进一步理解
堆:存储的是我们的对象
堆空间的分代划分
堆:分为新生代和老年代(划分的原因是因为垃圾回收,gc是垃圾回收机制,gc这个东西是自己自动执行的不需要手动调用)
新生代分为:Eden区(英:亦等),from区,to区
老年代是:Tenured(英:疼尼尔的)
堆的分代划分是真实的物理地址划分,且他们的物理地址是连贯的,彼此也是紧紧挨在一起。
对象进入堆空间的流程:当对象加载到堆里时,会被放到Eden区,(在假设一直都根可达的情况下)当被第一次垃圾回收时会放到from区,再一次被垃圾回收时会放到to区,此后每一次gc时就只会在from区和to区来回放置,直到15次后将放到tenured(老年代)区。
jvm对栈帧做的优化:当方法调用方法时,可能会操作相同的数据,jvm会在这个时候,栈帧与栈帧之间的局部变量表会拿出一部分来数据共享。典型就是调用a方法调用b方法传的参数用的是同一个操作数栈(只是说基本数据类型)。
内存溢出(oom:out of memory:慢么瑞)
栈内存溢出:栈区只能对单个栈的大小做限制,而无法对开启线程的数量作限制,所以栈溢出有两个原因,一个是不断的压入栈帧超过了单个栈的大小所报的溢出,一个是开启的栈超过了电脑还剩的内存。一般指的是第一种情况
堆内存溢出:堆的大小是可以做设置的,溢出的原因就是不断放入对象超出了堆大小。(抛出的异常信息:out of memory 后面加 java heap space),如果你再一个集合中不断的压入对象,导致垃圾回收占据98%的资源而回收不足2%的时候垃圾回收机制会报异常。
方法区内存溢出:方法区也是可以做大小设置的,当里面的类太多超出设定值时就会内存溢出。(抛出的异常信息:out of memory 后面加 metaspace)
本机直接内存溢出:byteBuffer可以操作直接内存,当给直接内存加以限制一个小值,所分派的内存大于这个小值的时候就会内存溢出,这只是个演示,一般实际场景多在测试服务器部署项目时发生。(抛出的异常信息:out of memory 后面加direct buffer memory)
常量池(方法区)
class常量池:放的是一些class的方法,还有class对应的一些字面量,还有符号引用。
运行时常量池:存放直接引用,在加载类加载时会把符号引用替换为直接引用,这些直接引用就放在运行时常量池当中。(在jdk1.7以后这个东西的实现放到了堆中间,但是规范是在方法区)。
字符串常量池:(规范正规的文档里是没有字符串常量池这个概念的,这是在hostop还有许多虚拟机中的一个优化)
1.String的是不可被修改的被final修饰,String是的值是一个被final修饰的char类型的一个数组,String还有一个int的hash值,为什么String要被这样设计呢?因为1.安全(因为不可修改) 2.因为String不会被改变所以他的hash值是唯一的 3.因为这样的设计可以才可以做字符串常量池
2.String a = "abc"; 当这行代码运行时,首先去字符串常量池中去找abc,如果有就把他指给引用,如果没有就在字符串常量池中创建一个abc然后指向引用。和堆没有任何关系
3.String b = new String("abc"); 当这行代码运行时,也会去找常量池看看有没有,然后会在堆中创建一个String对象,这个对象去指向字符串常量池的abc。让后这个对象在指向引用,这个写法不划算,因为会多创建一个对象
4.User user = new User(); user.setUserName("张三"); 这种写法和字符串常量池就没有关系了,是直接在对象中编辑,存放在堆中的对象中。
5.String c = "a"+"b"+"c"; 这种看代码来说,会在字符串常量中生成3个字符串a,ab,abc,效率很低,但是在代码编译过程中jvm会把这个写法优化成abc。但是String d = "a"; String e = "b"+"c"+d; 这种会优化成String e = "bc"+d; 而无法在进一步优化。
6.String a = new String("a").intern(); String b = new String.inertn("a"); 首先会创建一个对象然后字符串常量池会创建一个a, 其实第一个并不会有什么改变主要是 ,b在new的时候就不会在创建对象了,会直接把a引用给b,所以a和b是同一个引用地址。(intern的作用就是查找这个字符串是否有引用,如果有就直接返回)

浙公网安备 33010602011771号