JVM底层复习_针对面试(GC)

JVM:(Java虚拟机)
一次编译到处浪,跨平台性好,JVM的大致结构可以用JMM进行描述。

JVM底层复习_针对面试(GC)

JMM:(Java内存模型,一种抽象的说法)
堆:JVM所管理内存的最大一块内存区域,存放对象实例及数组,是所有线程共享的一块区域,堆内存=新生代+老年代+永久代,以gc的角度来看,我们往往将堆内存分为新生代和老年代。

新生代由Eden、From survovir、To survovir(survovir意思为幸存者),3者比例是8:1:1,但是JVM每次只会使用Eden和其中一块survovir区域来为对象服务,并保证一个survovir空闲,因此,新生代的实际可用内存空间为9/10。gc(垃圾回收机制)对于新生代采用复制算法进行垃圾回收,即Minor gc,Minor GC 是发生在新生代中的垃圾收集动作,所采用的是复制算法。gc对于Eden和其中一个survovir中 死亡 的对象,有责任进行垃圾回收,在Minor gc过后,对于未回收的对象,对象的年龄会+1,并采用复制算法将对象复制到另外一块 Survivor 区域,然后开始清理被回收的对象,当对象的年龄超过15岁时,就会被放入老年代。

老年代采用的是Full GC,Full GC采用的是标记清除算法,由于老年代要么占内存过大,要么内存中的对象存活过了15岁,Full GC进行一次垃圾回收的时间过长,所以不会频繁自动调用。另外,标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作。

JDK1.8之前
方法区:方法区也叫做‘永久代’ 它用于存储虚拟机加载的类信息、常量、静态变量,是各个线程共享的内存区域。默认内存容量的最小值为16MB,最大值为64MB。永久代和老年代是捆绑在一起的,因此无论谁满了,都会触发永久代和老年代的Full GC 进行标记整理算法的垃圾回收。

运行常量池:它是方法区的一部分,Class类文件中除了版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种符号的引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

JDk1.7开始移除永久代的移除工作,贮存在永久代中的一部分数据已经转移到了Java堆或者Native堆(本地堆),符号引用(Symbols)转移到了native 堆,字面量(interned strings)转移到了java 堆,类的静态变量(class statics)转移到了java 堆

JDK1.7 update 4版本提供了完整的支持对于Garbage-First(G1)垃圾收集器,之前采用的是CMS垃圾收集器(JDK5发布),G1仅仅在PermGen(永久代)满了或者应用分配内存的速度比G1并发垃圾收集速度快的时候才触发Full GC。

JDK1.8及之后
永久代:它被完全移除,永久代的参数-XX:PermSize和-XX:MaxPermSize也被移除了。

元空间:JVM内部表示,数据存储在Metaspace(元空间)的native memory(本地内存)
中,在本地内存进行分配,默认情况下,类的元数据只受可用的的本地内存限制(容量取决于是32/64位操作系统的可用虚拟内存大小)。Metaspace背后的一个思想是,类和它的元数据的
生命周期和它的类加载器生命周期一致,也是就是受说类加载器不在存活,被垃圾收集器声明死亡后,该类加载器对应的metaspace空间才可以回收。Metespace使用块分配器(chunking allocator)。其中有一个全局的可使用的块列表(a global free list of chunks)。当类加载器需要一个块的时候,类加载器从全局块列表中取出一个块,添加到它自己维护的块列表中。当类加载器死亡,它的块将会被释放,归还给全局的块列表。
元空间的特点:
1,每个加载器有专门的存储空间。
2,不会单独回收某个类。
3,元空间里的对象的位置是固定的。
4,如果发现某个加载器不再存货了,会把相关的空间整个回收。
为什么移除‘永久代’?
类的元数据信息转移到Metaspace的原因是PermGen很难调整。PermGen中类的元数据信息在每次FullGC的时候可能会被收集,但成绩很难令人满意。而且应该为PermGen分配多大的空间很难确定,因为PermSize的大小依赖于很多因素,比如JVM加载的class的总数,常量池的大小,方法的大小等。
元空间详细了解url:https://blog.csdn.net/zhushuai1221/article/details/52122880

栈:
虚拟机栈:它指的是java方法执行的内存模型的描述,每个方法在执行的过程中都会创建一个‘栈帧’用于存放局部变量表、操作栈、方法出口等信息。每个方法的调用到执行结束,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。生命周期与线程的相同,是线程私有的。

局部变量表:存放了编译器可知的各种基本数据类型、对象引用(引用指针,并非对象本身)。

本地方法栈:和虚拟机栈类似,区别在于虚拟机栈是为java虚拟机栈服务,而本地方法栈则是为Native方法服务。

程序计数器:
JVM中最小的一块内存区域,它的作用是当前线程所执行的字节码的行号指示器,在JMM中,字节码解释工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、异常处理、线程恢复等基础功能都需药依赖程序计数器来完成。

直接内存:
直接内存并不是虚拟机内存的一部分,也不是Java虚拟机规范中定义的内存区域。jdk1.4中新加入的NIO,引入了通道与缓冲区的IO方式,它可以调用Native方法直接分配堆外内存,这个堆外内存就是本机内存,不会影响到堆内存的大小.

原文链接:https://blog.csdn.net/u011972171/article/details/80398771

JVM底层复习_针对面试(GC)

并发编程
历史:
第一代:最初的计算机,是等待使编程人员思考之后,输入逻辑指令执行,在没有输入指令时是闲置的,缺点多
第二代:批处理操作系统,程序员封装逻辑指令写入磁盘,等待编程人员输入特定命令唤醒,交给cpu执行进行批处理,效率低
第三代:多进程操作系统,进程之间相互隔离,执行多个任务之间动态地切换进程,提高执行效率,但是一个进程每次只能执行一个任务,多个进程之间的切换时,会引发生进程命周期的变化,使进程重新开始执行,进程的生命周期(新建,就绪,运行,等待,终止),进程的切换涉及调度算法,虽然效率大大提升,仍然达不到应需求
第四代:多线程操作系统,线程成为计算机运行的最小单位,一个进程可包含多个状态,每个线程可执行一个任务,线程所占cpu资源小,执行多任务之间能进行线程的灵活切换,并且不会涉及生命周期的变化,但是容易发生多线程之间的线程串行,无法实现真正意义上的并行,体验度差,直到cpu双核的出现,线程之间能够完美地进行切换,完美解决线程串行,通过cpu时间片的调度,实现了真正意义上的并行。基本生命周期(新建,就绪,运行,堵塞,死亡)。

并发编程的三的特性:
原子性 是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。
可见性 是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
有序性 即程序执行的顺序按照代码的先后顺序执行。

————————以上属于个人理解,如有错误,请各位大神指正——————