亮色是所有线程共享,灰色是单个线程私有。
亮色存在垃圾回收,但是不一定有。
类加载器四个问题:
小class通过类加载器,变成大class,大class就装进了方法区,就是类的模板,通过实例化为多个car实例(实例多个,但是模板只有一个)
特定文件标识:类加载器并不是看文件后缀名是.class就将其加载进内存,还看文件内容开头是否含有特定的文件标识。如下图所示
要用一个类的话,先去顶部启动类加载器去找rt.jar 找不到就下去ext/*.jar拓展类加载器去找 ,再找不到就去应用类加载器去去找 再找不到就报错。
如果是jdk自带的类,走的是bootstrp(启动类加载器)。
如果是自己写的类,走应用程序类加载器。
bootstrp(启动类加载器) 也就是说 为啥可以用object string arraylist 都是因为已经通过bootstrp(启动类加载器) 加载进JVM里了。
看到 类扩展加载器 是APP加载器的祖先。
用户自定义类加载器,可以继承APP加载器 自己写。
双亲委派机制。虽然自己写了main方法 但是运行的时候先找他爸,直接去下面图找到string.class 先找到该类,就先使用,但是该类里没有main方法 所以报错。这样保证了自己写的代码不会污染java的源代码,保证了程序的安全性和健壮性。
接下来讲:
等号左边叫引用 在栈空间,右边new出来的实例对象在堆空间。一般说的栈是上图的java栈
进程只和操作系统有关,跟语言(C Java)无关
start0只有方法的声明,没有方法的实现。说明只要是native的方法,调用的是与Java无关的,底层的第三方的操作系统库,或者是C语言函数库。
由于方法要掉进栈里面(java栈),那么native方法要进本地方法栈里,不进java栈里(特殊的方法放进特殊的栈里)这个特殊的栈用来运行C语言。
接下来讲
register 寄存器 在CPU里的一小片区域里
PC register就是一个指针,记录了目前的方法运行完了,下一个要运行的方法是谁,就是一个班的排班值日表。main方法运行完 要运行下一个方法。。。为什么顺序不会出错,因为PC寄存器就是记录了各个方法之间的排班值日表。(存的是下一个将要运行的方法的地址)
方法区在哪 放了哪些东西,方法区不是放 方法的地方。
实例对象在堆里面
结构信息:一个类的字段、方法等。模板存在于方法区,方法区存储了类的结构信息(字节码内容)。
再看这个图
小class在磁盘里,通过JVM的类加载器加载成大class(变成了模板工厂)。
栈帧:java方法 当java方法压到栈里去的时候,方法就变成了栈帧。
当方法被压入栈里,那么依附在方法上的输入输出参数和方法里的变量肯定也在栈里面。
是错误不是异常:
逻辑上如上图所示,物理上只有两部分组成:新生+养老
7和8的不同 只有:7是永久区 8是元空间。
方法区相当于一个规范,java7落地实现是永久代,java8落地实现是元空间。
基本类型传递的是复印件,原件不动。
引用类型传的是同样的内存地址
一定要注意方法的作用域
所以第一题是:20
首先main方法里有一个age=20,随后进入changeValue1方法,将age复印一份给changeValue1方法,在该方法中age被修改为30,他修改的是复印件的值,不会修改main方法age原件的值,随后退出该方法。这时候该方法的age就没有了(死去了),随后回到main方法,这时候打印的还是age的值20.
这个内存地址里的真实值,被实心箭头这个指针修改成了xxx.
所以当changeValue2方法退出,main方法里的空心指针指的真实值为xxx
第三题:
字符串常量池:有,就直接复用,没有 就新建
所以str=abc 在常量池中新建一个,由于str是引用类型,所以传的是内存地址
但是由于常量池的原则是有就复用,没有就新建,所以由于常量池中没有xxx 所以要新建一个xxx ,所以实心的指针指向了新建的xxx.
当changeValue3退出时,main方法指向的还是abc 这道题很特殊。
调优
2.复制算法
3.标记清除
4.标记压缩
没有最好的算法,新生代和养老区用的不一样