@@ -55,10 +55,11 @@ JVM在内存新生代Eden Space中开辟了一小块线程私有的区域TLAB(
55555 . 初始化(执行类构造器、类变量赋值、静态语句块)
5656
5757#### 类加载器
58- 启动类加载器:是虚拟机自身的一部分,它负责将 <JAVA_HOME>/lib路径下的核心类库
59- 扩展类加载器:它负责加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发者可以直接使用标准扩展类加载器
60- 系统类加载器:它负责加载系统类路径java -classpath或-D java.class.path 指定路径下的类库
58+ 启动类加载器:用C++语言实现, 是虚拟机自身的一部分,它负责将 <JAVA_HOME>/lib路径下的核心类库,无法被Java程序直接引用
59+ 扩展类加载器:用Java语言实现, 它负责加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发者可以直接使用
60+ 系统类加载器:用Java语言实现,它负责加载系统类路径ClassPath指定路径下的类库,开发者可以直接使用
6161
62+ #### 双亲委派
6263定义:如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是** 双亲委派模式** 。
6364
6465优点:采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次防止恶意覆盖Java核心API。
@@ -70,7 +71,7 @@ JVM在内存新生代Eden Space中开辟了一小块线程私有的区域TLAB(
70713 . 为了实现代码热替换,OSGi是为了实现自己的类加载逻辑,用平级查找的逻辑替换掉了向下传递的逻辑。但其实可以不破坏双亲委派逻辑而是自定义类加载器来达到代码热替换。比如[ 这篇文章] ( https://www.cnblogs.com/pfxiong/p/4070462.html )
7172
7273### 内存分配(堆上的内存分配)
73- ![ ] ( https://github.com/xbox1994/2018-Java-Interview/raw/master/images/j2.jpg )
74+ ![ ] ( https://github.com/xbox1994/2018-Java-Interview/raw/master/images/堆的内存分配.png )
7475#### 新生代
7576##### 进入条件
7677优先选择在新生代的Eden区被分配。
@@ -106,36 +107,6 @@ GC Roots包括:虚拟机栈中引用的对象、方法区中类静态属性引
1061072 . 老年代空间不足(通过Minor GC后进入老年代的大小大于老年代的可用内存)
1071083 . 方法区空间不足
108109
109- ## [ Java内存模型] ( https://mp.weixin.qq.com/s/ME_rVwhstQ7FGLPVcfpugQ )
110- 定义:JMM是一种规范,目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。目的是保证并发编程场景中的原子性、可见性和有序性
111-
112- 实现:volatile、synchronized、final、concurrent包等。其实这些就是Java内存模型封装了底层的实现后提供给程序员使用的一些关键字
113-
114- ![ ] ( https://github.com/xbox1994/2018-Java-Interview/raw/master/images/j12.jpg )
115-
116- 主内存:所有变量都保存在主内存中
117- 工作内存:每个线程的独立内存,保存了该线程使用到的变量的主内存副本拷贝,线程对变量的操作必须在工作内存中进行
118-
119- 每个线程都有自己的本地内存共享副本,如果A线程要更新主内存还要让B线程获取更新后的变量,那么需要:
120-
121- 1 . 将本地内存A中更新共享变量
122- 2 . 将更新的共享变量刷新到主内存中
123- 3 . 线程B从主内存更新最新的共享变量
124-
125- ## [ happens-before] ( https://www.cnblogs.com/chenssy/p/6393321.html )
126- 我们无法就所有场景来规定某个线程修改的变量何时对其他线程可见,但是我们可以指定某些规则,这规则就是happens-before。特别关注在多线程之间的内存可见性。
127-
128- 它是判断数据是否存在竞争、线程是否安全的主要依据,依靠这个原则,我们解决在并发环境下两操作之间是否可能存在冲突的所有问题。
129-
130- 1 . 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;
131- 2 . 锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作;
132- 3 . volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
133- 4 . 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
134- 5 . 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
135- 6 . 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
136- 7 . 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
137- 8 . 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;
138-
139110## 垃圾收集器
140111### 串行收集器
141112串行收集器Serial是最古老的收集器,只使用一个线程去回收,可能会产生较长的停顿
@@ -175,6 +146,5 @@ STW总会发生,不管是新生代还是老年代,比如CMS在初始标记
175146
176147那么为什么一定要STW?因为在定位堆中的对象时JVM会记录下对所有对象的引用,如果在定位对象过程中,有新的对象被分配或者刚记录下的对象突然变得无法访问,就会导致一些问题,比如部分对象无法被回收,更严重的是如果GC期间分配的一个GC Root对象引用了准备被回收的对象,那么该对象就会被错误地回收。
177148
178- ## JVM调优
179- http://www.wangtianyi.top/blog/2018/07/25/jvmdiao-you-ru-men- (yi-): ji-chu-zhi-shi /
180- http://www.wangtianyi.top/blog/2018/07/27/jvmdiao-you-ru-men- (er-): shi-zhan-diao-you-parallelshou-ji-qi /
149+ ## [ JVM调优] ( http://www.wangtianyi.top/blog/2018/07/27/jvmdiao-you-ru-men-(er-):shi-zhan-diao-you-parallelshou-ji-qi/?utm_source=github&utm_medium=github )
150+
0 commit comments