JVM 深入浅出

jvm 相信大家都有认知 在面试中也是经常遇见的希望接下来的这篇文章可以对你们带来帮助

java 的好处 :

java这门语言之所以受广大的码农所喜爱是因为它不像C语言需要程序员自己手动分配内存空间 java 100% 专注让程序员业务代码 ,而c语言需要50%关注内存的分配

话不多说 先来一道面试题:

在运行时数据区 哪些是线程是共享的哪些线程私有的 

  •  线程共享的包括 :堆和 方法区
  •  线程私有的包括 :虚拟机栈 本地方法栈 程序计数器

程序计数器:指向当前线程正在执行的字节码指令的地址也就是 执行到哪一行的行号 唯一的一个不会导致 OutofMemoryError

虚拟机栈 :基本数据类型和对象的引用变量都存在此区域 栈是一种数据结构 先进后出,后进先出 一个方法就是一个栈 栈的单位是栈帧

本地方法栈

 简单点说就是 底层用native修饰的 底层已经用c语言进行编写的

方法区 :

  1. 所有线程共享;
  2. 方法(包含构造),接口,定义在此区域
  3. 所有方法的信息;
  4. 静态常量类信息+方法信息+常量池
  5. 在jdk1.8以后 常量不再存储在方法区了 而存储在接下来要说的堆中

堆可以说是最繁琐的了 因为它里面包括了一些特别的饿数据结构和算法

所有对象的实例和数组开辟的空间都在此区域保存  所有线程共享

堆分区: (他们的比例8:1:1)

  1. 新生代

  2.老年代

  3.永久代

Java中内存分配机制

核心:分代分配,分代回收,分代整理

 

新生代(Young Generation

 

 

 说到JVM 不得不提的两个异常

StackOverFlowerror

线程请求的栈的深度大于虚拟机栈规定的深度

OutofMemoryError

虚拟机在扩展栈时,无法申请到足够的内存空间

 

GC的深入浅出

垃圾回收机制的定义

 java程序运行过程中 jvm有一个专门负责回收那些不再使用的对象所占有的内存这种回收 过程我们称之为垃圾回收机制

 

对象的状态

java虚拟机的垃圾回收器来看,堆中的对象有三种状态

01 可触及状态

02 可复活状态

03 不可触及状态

只有对象处于不可触及状态的时候,垃圾回收期才会真正的师傅对象所占的内存

对象的生命周期开始(new语句 或者反射中的newInstance)  可触及状态

对象不再被引用,或者对象调用了finallize()     可复活状态

对象调用了finalize()        不可触及状态

垃圾回收

对象的生命周期结束

垃圾何时被回收(触发GC 的条件)

对象没有引用====>对象处于不可触及的状态

程序在作用域正常执行之后

System.exie()

程序以外终止

什么时间==触发GC条件

  1.  System.gc()可能触发
  2. 系统自身决定GC的触发时间
  3. 根据Eden区和FromSpace区的内存大小来决定对什么东西

JVM GC回收哪个区域的垃圾?

需要注意的是,JVM GC只回收堆区和方法区内的对象。而栈区的数据,在超出作用域后会被JVM自动释放掉,所以其不在JVM GC的管理范围内。

JVM GC怎么判断对象可以被回收?

1. 对象没有引用

2. 作用域发生未捕获异常

3. 程序在作用域正常执行完毕

4. 程序执行了System.exit()

5. 程序发生意外终止(被杀线程等)

6. Java程序中不能显示的分配和注销缓存,因为这些事情JVM都帮我们做了,那就是GC。有时候我们可以将相关的对象设置成null来试图显式的清楚缓存,但是并不是设置成null就会一定被标记成可回收,有可能发生逃逸。将对象设置成null至少没有什么坏处,但是使用System.gc()便不可取了,使用System.gc()时并不是马上执行GC操作,而是会等待一段时间,甚至不执行,而且System.gc()如果被执行,会触发Full GC,这非常影响性能。

JVM GC什么时候执行?

eden区空间不够存放新对象的时候,执行Minro GC。升到老年代的对象大于老年代剩余空间的时候执行Full GC,小于时被HandlePromotionFailure参数强制full gc。调优主要是减少Full GC的触发次数,可以通过NewRatio控制新生代转老年代的比例,通过Max Tenuring Threshold设置对象进入老年代的年龄阀值。

 

减少GC开销的措施

根据上述GC的机制,程序的运行会直接影响系统环境的变化,从而影响GC的触发。若不针对GC的特点进行设计和编码,就会出现内存驻留等一系列负面影响。为了避免这些影响,基本的原则就是尽可能地减少垃圾和减少GC过程中的开销。具体措施包括以下几个方面:

(1) 不要显式调用System.gc()

此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。

(2) 尽量减少临时对象的使用

 临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。

(3) 对象不用时最好显式置为Null

一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。

(4) 尽量使用StringBuffer,而不用String来累加字符串

由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象。

(5) 能用基本类型如Int,Long,就不用Integer,Long对象

 基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。

(6) 尽量少用静态对象变量

 静态变量属于全局变量,不会被GC回收,它们会一直占用内存。

(7) 分散对象创建或删除的时间

集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。

 

posted @ 2018-11-26 18:12  HelloWord404  阅读(195)  评论(0编辑  收藏  举报