菜鸟笔记
提升您的技术认知

java如何关闭gc,gc垃圾回收机制-ag真人游戏

gc垃圾回收机制,

个人理解:

因为在使用java创建一个类或者对象后,难免会存在以后不使用的情况,为了减少其继续再占用内存,必须建立一套清理垃圾的机制,但是怎么判断什么样的才算是不使用的垃圾呢,这里面进行了判断并标记分类,然后根据不同的标记再进行不同的处理。不过世事无完美之说,其也是存在弊端的(开销通常很大,而且它的运行具有不确定性),为了避免,我们还是在正常工作中,养成一个好的编程习惯。其都解释的比较详细!

一、java内存区域:

在java运行时的数据区里,由jvm(java虚拟机)管理的内存区域分为下图几个模块:

其中:

1,程序计数器(program counter register):程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。

每个程序计数器只用来记录一个线程的行号,所以它是线程私有(一个线程就有一个程序计数器)的。

如果程序执行的是一个java方法,则计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是一个本地(native,由c语言编写完成)方法,则计数器的值为undefined,由于程序计数器只是记录当前指令地址,所以不存在内存溢出的情况,因此,程序计数器也是所有jvm内存区域中唯一一个没有定义outofmemoryerror的区域。

2,虚拟机栈(jvm stack):一个线程的每个方法在执行的同时,都会创建一个栈帧(statck frame),栈帧中存储的有局部变量表、操作站、动态链接、方法出口等,当方法被调用时,栈帧在jvm栈中入栈,当方法执行完成时,栈帧出栈。

局部变量表中存储着方法的相关局部变量,包括各种基本数据类型,对象的引用,返回地址等。在局部变量表中,只有long和double类型会占用2个局部变量空间(slot,对于32位机器,一个slot就是32个bit),其它都是1个slot。需要注意的是,局部变量表是在编译时就已经确定好的,方法运行所需要分配的空间在栈帧中是完全确定的,在方法的生命周期内都不会改变。

虚拟机栈中定义了两种异常,如果线程调用的栈深度大于虚拟机允许的最大深度,则抛出statckoverflowerror(栈溢出);不过多数java虚拟机都允许动态扩展虚拟机栈的大小(有少部分是固定长度的),所以线程可以一直申请栈,直到内存不足,此时,会抛出outofmemoryerror(内存溢出)。

每个线程对应着一个虚拟机栈,因此虚拟机栈也是线程私有的。

3,本地方法栈(native method statck):本地方法栈在作用,运行机制,异常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中(如sun的jdk默认的hotspot虚拟机),会将本地方法栈与虚拟机栈放在一起使用。

本地方法栈也是线程私有的。

4,堆区(heap):堆区是理解java gc机制最重要的区域,没有之一。在jvm所管理的内存中,堆区是最大的一块,堆区也是java gc机制所管理的主要内存区域,堆区由所有线程共享,在虚拟机启动时创建。堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存(不过现代技术里,也不是这么绝对的,也有栈上直接分配的)。

一般的,根据java虚拟机规范规定,堆内存需要在逻辑上是连续的(在物理上不需要),在实现时,可以是固定大小的,也可以是可扩展的,目前主流的虚拟机都是可扩展的。如果在执行垃圾回收之后,仍没有足够的内存分配,也不能再扩展,将会抛出outofmemoryerror:java heap space异常。

关于堆区的内容还有很多,将在下节“java内存分配机制”中详细介绍。

5,方法区(method area):在java虚拟机规范中,将方法区作为堆的一个逻辑部分来对待,但事实上,方法区并不是堆(non-heap);另外,不少人的博客中,将java gc的分代收集机制分为3个代:青年代,老年代,永久代,这些作者将方法区定义为“永久代”,这是因为,对于之前的hotspot java虚拟机的实现方式中,将分代收集的思想扩展到了方法区,并将方法区设计成了永久代。不过,除hotspot之外的多数虚拟机,并不将方法区当做永久代,hotspot本身,也计划取消永久代。本文中,由于笔者主要使用oracle jdk6.0,因此仍将使用永久代一词。

方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。

方法区在物理上也不需要是连续的,可以选择固定大小或可扩展大小,并且方法区比堆还多了一个限制:可以选择是否执行垃圾收集。一般的,方法区上执行的垃圾收集是很少的,这也是方法区被称为永久代的原因之一(hotspot),但这也不代表着在方法区上完全没有垃圾收集,其上的垃圾收集主要是针对常量池的内存回收和对已加载类的卸载。

在方法区上进行垃圾收集,条件苛刻而且相当困难,效果也不令人满意,所以一般不做太多考虑,可以留作以后进一步深入研究时使用。

在方法区上定义了outofmemoryerror:permgen space异常,在内存不足时抛出。

运行时常量池(runtime constant pool)是方法区的一部分,用于存储编译期就生成的字面常量、符号引用、翻译出来的直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址,将在类链接阶段完成翻译);运行时常量池除了存储编译期常量外,也可以存储在运行时间产生的常量(比如string类的intern()方法,作用是string维护了一个常量池,如果调用的字符“abc”已经在常量池中,则返回池中的字符串地址,否则,新建一个常量加入池中,并返回地址)。

6,直接内存(direct memory):直接内存并不是jvm管理的内存,可以这样理解,直接内存,就是jvm以外的机器内存,比如,你有4g的内存,jvm占用了1g,则其余的3g就是直接内存,jdk中有一种基于通道(channel)和缓冲区(buffer)的内存分配方式,将由c语言实现的native函数库分配在直接内存中,用存储在jvm堆中的directbytebuffer来引用。由于直接内存收到本机器内存的限制,所以也可能出现outofmemoryerror的异常。

java对象的访问方式

一般来说,一个java的引用访问涉及到3个内存区域:jvm栈,堆,方法区。

以最简单的本地变量引用:object obj = new object()为例:

①、object obj表示一个本地引用,存储在jvm栈的本地变量表中,表示一个reference类型数据;

②、new object()作为实例对象数据存储在堆中;

③、堆中还记录了object类的类型信息(接口、方法、field、对象类型等)的地址,这些地址所执行的数据存储在方法区中;

二、需要gc的原因:

应用程序对资源操作,通常简单分为以下几个步骤:

1、为对应的资源分配内存

2、初始化内存

3、使用资源

4、清理资源

5、释放内存

应用程序对资源(内存使用)管理的方式,常见的一般有如下几种:

1、手动管理:c,c

2、计数管理:com

3、自动管理:.net,java,php,go…

但是,手动管理和计数管理的复杂性很容易产生以下典型问题:

1.程序员忘记去释放内存

2.应用程序访问已经释放的内存

产生的后果很严重,常见的如内存泄露、数据内容乱码,而且大部分时候,程序的行为会变得怪异而不可预测,还有access violation等。

.net、java等给出的ag真人游戏的解决方案,就是通过自动垃圾回收机制gc进行内存管理。这样,问题1自然得到解决,问题2也没有存在的基础。

总结:无法自动化的内存管理方式极容易产生bug,影响系统稳定性,尤其是线上多服务器的集群环境,程序出现执行时bug必须定位到某台服务器然后dump内存再分析bug所在,极其打击开发人员编程积极性,而且源源不断的类似bug让人厌恶。

三、gc如何工作:

gc的工作流程主要分为如下几个步骤:

1、标记(mark)---gc的根节点也即gc root

2、计划(plan)

3、清理(sweep)

4、引用更新(relocate)

5、压缩(compact)

在java中,可以当做gc root的对象有以下几种:

1、虚拟机(jvm)栈中的引用的对象

2、方法区中的类静态属性引用的对象

3、方法区中的常量引用的对象(主要指声明为final的常量值)

4、本地方法栈中jni的引用的对象

四、什么时候发生gc;

1、当应用程序分配新的对象,gc的代的预算大小已经达到阈值,比如gc的第0代已满

2、代码主动显式调用system.gc.collect()

3、其他特殊情况,比如,windows报告内存不足、clr卸载appdomain、clr关闭,甚至某些极端情况下系统参数设置改变也可能导致gc回收

五、建议:

由于gc的代价很大,平时开发中注意一些良好的编程习惯有可能对gc有积极正面的影响,否则有可能产生不良效果。

1、尽量不要new很大的object,大对象(>=85000byte)直接归为g2代,gc回收算法从来不对大对象堆(loh)进行内存压缩整理,因为在堆中下移85000字节或更大的内存块会浪费太多cpu时间。

2、不要频繁的new生命周期很短object,这样频繁垃圾回收频繁压缩有可能会导致很多内存碎片,可以使用设计良好稳定运行的对象池(objectpool)技术来规避这种问题。

3、使用更好的编程技巧,比如更好的算法、更优的数据结构、更佳的解决策略等等。

相关文章暂无相关文章

网站地图