`
xmtkhlwk
  • 浏览: 3912 次
  • 性别: Icon_minigender_1
  • 来自: 福建
最近访客 更多访客>>
社区版块
存档分类
最新评论

java内存管理原理

阅读更多
作为java开发人员,很少会去关心内存是如何分配与回收,java虚拟机为我们做好了一切,但并不代表开发人员可以对java内存管理的原理一无所知,java应用程序是很消耗内存的,特别是对Sever模式的应用程序,当并发高,运行时间长的时候,代码中内存的浪费也会导致应用程序的停顿、事务执行的失败。因此,了解java内存的管理对于编码的习惯,jvm垃圾回收器的选择等有助于应用程序效率的提高有很大的帮助。
基础知识—java内存块简述
Java虚拟机的内存主要分为以下几块
一、 堆内存
堆内存是java内存块中最大的一块内存,也是java虚拟机内存管理的主要内存块,它存放对象的实例,线程共享。想想看,我们应用程序中有成千上万个对象,每个对象可能被实例化成几十个乃至上百个,可能还更多。
我们来做一个简单的计算:一个bean对象有7个string型属性,对象是简单的getter/setter方法,大小是187byte。通过如下代码可以判断一个对象的大小:
public void testSize() {
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(baos);
			oos.writeObject(new OOMObject());
			byte[] bs = baos.toByteArray();
			int size = bs.length - 4;// 对象大小
			System.out.println(size);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}


如果一个应用程序有5W个这样的对象,实例化100个。那么它占据堆内存大小为:
(187*50000*100)/(1024*1024)=891.69M
这个数据可以简单的想象下,当创建大量string对象,那么内存的消耗相当可观,因此在程序当中,要注意对象的创建,能重用的就重用,如String的对象最好用stringbuffer来代替等等。
那么堆内存是如何管理的呢,先从堆内存的结构说起:
(一) 结构:
堆内存可以分为新生代和老年代,新生代又可以分为Eden区、From  Survivor区、To Survivor区。
(二) 异常:
会抛出OutOfMemoryError并进一步提示Java heap space.抛出异常原理及例子请看第四部分.
(三) 管理与回收原理:
Jvm对新生代和老年代有不同的管理方式和算法,也有不同的垃圾回收器进行回收。
1. 新生代
这个区的大小可以通过-XX:NewRatio来设置,例如:-XX:NewRatio=4表示新生代和老代的比例是1:4。新生代的垃圾回收管理一般采用复制算法。这个算法是将内存按容量划分为大小相等的两块,每次只使用其中的一块。当这块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
基于这种算法,JVM把新生代分为一块较大的Eden空间和两块较小的Survivor空间.
1) 实例化一个对象时,首先会优先在Eden区域分配内存,但当这个对象很大的时候则直接进入老年代。
a) 对象大到什么程度才会进入老年代呢?默认的大小是Eden的大小,但是可以通过-XX:PretenureSizeThreshold参数设置,如 -XX:PretenureSizeThreshold=3145728,这个参数不能与-Xmx之类的参数一样直接写3MB.看如下代码.
/**
* 参数:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:MaxTenuringThreshold=15 -XX:SurvivorRatio=8 -XX:+PrintTenuringDistribution  -XX:PretenureSizeThreshold=3145728B
	 */
	private void bigObject(){
		int _1M=1024*1024;
		byte[] allo1;
		//分配3M,直接进入老年代.
		allo1=new byte[3*_1M];
		
	}

b) 如何设置Eden的大小?通过比例Eden:Survivor的比例来设置,默认是8:1.例如-XX:SurvivorRatio=8,表示1个Survivor和Eden的比值为1:8
2) 在Eden分配内存,首先要判断空间是否足够,如果不够则会进行一次Minor GC,以释放内存。释放内存后还是不够分配,则直接进入老年代。Minor GC中内存的调配,发生了以下动作:
a) JVM会进行一次复制算法,把不能回收的对象,也就是还有引用的对象复制到第二块Survivor,这时要注意当第二块Survivor空间不足以存放复制对象时,则复制对象会晋升到老年代。请看如下代码:
/**
	 * 新生代不够分配,就直接分配到老年代,老年代还是不够直接报错
	 * 参数:-Xms30M -Xmx30M -Xmn10M -XX:+PrintGCDetails
	 */
	private void notEnoughEdenAndOld(){
		int _1M=1024*1024;
		//1:分配6M到新生代:Eden和一个Survivor
		byte[] _6M=new byte[6*_1M];
		
		//2:欲分配7M空间。
		//3:发现新生代空间不够,触发一次Minor GC,
		//先前分配的6M空间对象还有引用,故不能GC掉,基于复制的算法,
//把6M的对象复制到第二块Survivor,第二块Survivor只有1M空间,不够存放6M的内容,
		//则6M对象直接进入老年代。
		//4:此时新生代够存放7M对象,7M对象分配给新生代
		//5:这时候新生代7M,老年代6M
		byte[] _7M=new byte[7*_1M];
		_6M=null;
		//6:欲在新生代分配6M空间,不够(见第3点描述)。
		//7:发生一次MinorGC。
		//8:在GC的过程中,7M的空间晋升老年代,6M分配给新生代。
		//9:这时候新生代6M,老年代7+6=13M,其中6M可以回收。
		byte[] _6M2=new byte[6*_1M];
		_7M=null;
		//10:欲在新生代分配8M空间,不够(见第3点描述)
		//11:发生一次MinorGC
		//12:在GC的过程中,6M的空间晋升老年代。老年代6+13=19M,其中6+7=13M可以回收
		//13:此时新生代没有对象,老年代8M+7M=15M
		byte[] _8=new byte[8*_1M];
	}

b) 长期存活的对象将进入老年代。长期存活的对象默认是15岁,也就是经过了15次Minor GC还存在的对象,可以通过-XX:MaxTenuringThreshold来设置,例如-XX:MaxTenuringThreshold=1,意思是当MinorGC一次后,就直接把改对象放入老年代。参考如下代码:
/**
* 参数:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:MaxTenuringThreshold=1 -XX:SurvivorRatio=8
	 */
	private void notEnoughEdenAndOld1(){
		int _1M=1024*1024;
		byte[] allo1,allo2,allo3;
		//1:分配256到新生代:Eden和一个Survivor
		allo1=new byte[_1M/4];
		//2:欲分配4M空间。
		//3:发现新生代空间足够,直接分配到新生代。
		//4:这时候新生代4.25M。
		allo2=new byte[4*_1M];
		//5:欲分配5M空间。
		//6:发现新生代空间不够,触发一次Minor GC,
		//先前分配的4.25M空间对象还有引用,故不能GC掉,基于复制的算法,
		//把4.25M的对象复制到第二块Survivor。
		//7:此时新生代不够存放7M对象,7M对象分配给老年代
		//8:这时候老年代4.25M,新生代5M,并且标记为1岁
		allo3=new byte[5*_1M];
		//9:欲分配4M空间。
		//10:发现新生代空间不够,触发一次Minor GC,
//新生代中5M的对象已经被标记为1岁,直接进入老年代,5524K>0K(9216K)完美清0,如果是-XX:MaxTenuringThreshold=15的话则不会清0
		//11:这时候老年代5+4=9M,新生代4.25M
		allo3=new byte[4*_1M];
	}

c) 动态年龄对象晋升到老年代:
描述如下:如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或者等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄.参考如下代码:
/**
* 参数:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:MaxTenuringThreshold=15 -XX:SurvivorRatio=8
	 */
	private void notEnoughEdenAndOld2(){
		int _1M=1024*1024;
		byte[] allo1,allo2,allo3,allo4;
		//1:分配256到新生代:Eden和一个Survivor
		allo1=new byte[_1M/4];
		
		allo2=new byte[_1M/4];
		
		allo3=new byte[4*_1M];
		//触发一次MinorGC,allo1、allo2、allo3被标记为1岁
		//晋升到老年代的是allo3对象
//这时候老年代的大小差不多是4M-256*2K(剩余allo1、allo2和部分allo3对象在另一块Survivor空间),新生代的大小是4M+256*2K
		allo4=new byte[4*_1M];
		allo4=null;
//触发一次MinorGC,allo1、allo2的等于Survivor空间的一半,因为allo1、allo2和部分allo3晋升到老年代
		//allo4被回收
//新生代大小是4M,老年代大小是4M-256*2M+256*2M+部分的allo3,结果是4M
		allo4=new byte[4*_1M];
	}

d) 什么样的对象是不能回收的对象呢?JVM是使用根搜索算法(GC RootsTracing)判断对象是否存活的。这个算法的基本思路是通过一系列的名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
2. 老年代
老年代的垃圾回收与管理效率不高,大概是新生代的十分之一左右,这跟算法有关,老年代不能采用复制的算法,原因有2个:
1:复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低.而老年代存放的大部分是长久存活的对象,会倒是较多的复制操作.
2:当对象100%存活的情况下,第二块Survivor空间将很大可能放不下,需要额外的空间担保.老年代以外已经没有额外的空间担保了.
因此老年代一般采用采用标记-清除算法或者是标记整理算法.
1) 标记-清除:分为标记和清楚两个阶段:首先标记处所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象.它有2个缺点:
a) 效率问题,标记和清除过程的效率都不高.
b) 空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作.
2) 标记-整理:该算法的标记过程和标记-清除的标记一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存.效率会比标记-清除算法来的高.
晋升到老年代的对象,当碰到如下情况会发生FULL GC:
1:老年代空间不足,看如下代码:
/**
	 * 参数:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails
	 */
	private void old(){
		int _1M=1024*1024;
		byte[] allo1,allo2,allo3;
		//大对象直接进入老年代
		allo1=new byte[8*_1M];
		//新生代足够存放2M
		allo2=new byte[2*_1M];
		//分配7M给新生代,但是不够分配,触发一次Minor GC,
//新生代的2M晋升到老年代,此时老年代空间不够,触发一次FULL GC,对象没有释放,空间不够,直接报错
		allo3=new byte[7*_1M];
		
	}

2:老年代空间足够,但是不允许担保失败,看如下代码:
/**
* -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:-HandlePromotionFailure
	 */
	private void old(){
		int _1M=1024*1024;
		byte[] allo1,allo2,allo3,allo4,allo5,allo6,allo7;
		//2M进入新生代
		allo1=new byte[2*_1M];
		//2M进入新生代,总共有4M
		allo2=new byte[2*_1M];
		//触发一次MinoGC,4M进入老年代
		//这时候新生代4M,老年代4M
		allo3=new byte[4*_1M];
		//3M进入新生代
		//这时候新生代7M,老年代4M
		allo4=new byte[3*_1M];
		
		allo3=null;
		allo2=null;
//触发一次MinoGC,回收6M空间(allo3+allo2),另外2M空间进入老年代,参数设置不允许担保
		//2M<老年代剩余空间,因此会触发一次FULL GC
		allo5=new byte[3*_1M];
	}

3:不够空间存放大对象,看如下代码:
/**
	 * 参数:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails
	 */
	private void old(){
		int _1M=1024*1024;
		byte[] allo1,allo2,allo3;
		//新生代和老年代都不够存放,触发FULL GC,并且报错
		allo1=new byte[20*_1M];
	}

3. 分配过程如下图:


二、 方法区:
方法区内存块存放的是被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。它是线程共享的。这块内存会导致OutOfMemoryError:PermGen space。
以下的操作为导致内存溢出:
1:使用CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成的class可以加载到内存。
2:大量JSP或动态产生JSP文件的应用。
3:常量池的溢出。请看如下代码:
/**
	 * 常量池溢出模拟
	 * 参数-XX:PermSize=10M -XX:MaxPermSize=10M -XX:+PrintGCDetails
	 */
	private void changliangchi(){
		List<String> list=new ArrayList<String>();
		int i=0;
		while(true){
			list.add(String.valueOf(i++).intern());
		}
		
	}

String.intern这个方法的意思是:如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。如果不用intern这个方法上述代码将抛出堆内存溢出的错误。

垃圾回收分为2部分内容:
1:废弃常量。没有地方引用的常量.
2:无用的类。
1:该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
2:加载该类的ClassLoader已经被回收。
3:该类对于的java.lang.Class对象没有任何地方被引用,无法再任何地方通过反射访问该类的方法
三、 栈:
Java虚拟机栈是线程私有的,它的生命周期与线程相同。每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。所以不会出现栈帧内存溢出的异常。
局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,根据不同的虚拟机实现,它可能是一个指向对象起始地址的引用指针,也可能指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(返回对象的类型)。
局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
栈会抛出2中异常:
1:StackOverflowError。
如果线程请求的栈深度大于虚拟机所允许的最大深度,抛出异常。
2:OutOfMemoryError
如果虚拟机在扩展时无法申请到足够的内存空间,抛出异常。
可以通过-Xss参数来模拟这2中异常的抛出。
-Xss128K:减少栈内存大小,减少128K。
那么通过上面的描述可以知道,所有线程的栈是固定的,那么当每个线程的栈比较大(也就是说-Xss设置的比较小),那么能分配到的线程就比较少。这时候可能抛出第二种异常。如下代码:
/**
	 * 栈异常测试
	 * 参数-Xms1024M -Xmx1024M -XX:PermSize=256M -XX:MaxPermSize=256M -Xss83201K
	 */
	public static void main(String[] args) throws Throwable{
		//不会打印12,因为栈内存设为0,则线程请求不到内存分配,直接抛出异常。
		System.out.println(12);
		JvmErrorTest jvmErrorTest=new JvmErrorTest();
		try {
			jvmErrorTest.methodStack();
		} catch (Throwable e) {
			System.out.println(jvmErrorTest.stackLength);
			throw e;
		}
	}
	private void methodStack(){
		stackLength++;
		methodStack();
	}

如果-Xss设置的比较大,则每个线程栈内存就比较小,那么线程分配的多,这种时候就可以尽量避免第二种错误,但这种分配栈内存小,也就意味着每个栈的栈深度比较小,就有可能出现第一种情况异常,如下代码:
/**
	 * 栈异常测试
	 * 参数-Xms1024M -Xmx1024M -XX:PermSize=256M -XX:MaxPermSize=256M -Xss128K
	 */
	public static void main(String[] args) throws Throwable{
		JvmErrorTest jvmErrorTest=new JvmErrorTest();
		try {
			jvmErrorTest.methodStack();
		} catch (Throwable e) {
			// TODO: handle exception
			System.out.println(jvmErrorTest.stackLength);
			throw e;
		}
	}
	private void methodStack(){
		stackLength++;
		//递归调用,当达到最大栈深度,则报错
		methodStack();
	}

实际上第一种异常也可以看出是内存的溢出,只不过JVM把这种内存的溢出转化成栈深度的溢出。
程序计数器:
它是一块比较小的内存空间,作用是当前线程所执行的字节码的行号指示器。也是线程私有的。不会出现内存异常情况。
Java虚拟机垃圾回收器的选择:
新生代垃圾回收器和老年代垃圾回收器的搭配图:

单线程垃圾回收器:(单CPU环境下效果比较好,一般应用与Client模式)
新生代
Serial收集器
该收集器采用复制的算法,单线程的意义不仅仅意味着它只会使用一个CPU或者是一条线程去完成垃圾回收工作,更重要的是在它进行垃圾回收时,会暂停其他所有的工作线程,这样会造成应用程序的停顿,带给用户恶劣的体验,俗称“Stop The World”.
老年代
Serial Old收集器:
使用标记-整理算法,该收集器也可以用在Server模式下,一个是在JDK1.5及之前的版本中与Parallel Scavenge收集器搭配使用(Parallel Scavenge收集器本身有PS MarkSweep收集器来进行老年代收集,并没有直接使用Serial Old收集器,但是这个PS MarkSweep收集器是以Serial Old收集器为模板设计的,与Serial Old的实现非常接近);另一个是做为CMS收集器的后备方案,在并发手机发生Concurrent Mode Failure的时候使用。
多线程垃圾回收器:(大于1个的CPU效果比较好,一般应用与Server模式)
新生代
ParNew收集器
该收集器跟相差不大,唯一一个区别就是多线程,它是一个并行收集器。
并行:指多条垃圾收集线程并行工作,但用户线程仍然处于等待状态。
并发:指用户线程与垃圾收集线程同时执行。
Parallel Scavenge收集器
该收集器使用复制算法,也是并行的多线程收集器,它跟ParNew收集器有什么区别呢?关注点不同,Parallel Scavenge收集器关注吞吐量,所谓的吞吐量指的是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),如虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
老年代
Parellel Old收集器
该收集器是1.6版本才提供和Parallel Scavenge搭配使用
CMS收集器
该收集器是一种以获取最短回收停顿时间为目标的收集器。一般用在互联网或者B/S系统的服务端上。它是基于“标记-清楚”算法实现的。默认启动线程是(CPU数量+3)/4。当CPU在4个以上时,并发回收时垃圾收集线程最多占用不超过25%。如果CPU不足4个时,那么CMS对用户程序的影响就可能变得很大。
缺点:
1:无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生,并且启用备用垃圾回收器Serial Old,这样停顿时间就很长了。
浮动垃圾:由于CMS并发清理阶段用户线程还在运行着,伴随程序的运行自然还会产生新的垃圾,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理掉它们。这一部分垃圾就称为“浮动垃圾”。
默认情况下,CMS收集器在老年代使用了68%的空间后就会被激活。
如果在应用中老年代增长的不是很快,可以适当的提高激活比例,通过参数设置:
-XX:CMSInitiatingOccupancyFraction。
2:收集过后,会产生大量的空间碎片。这种情况会对大对像的分配造成麻烦,往往会出现老年代还有很大的剩余空间,但是无法找到足够大的连续空间来分配大对象,不得不触发一次Full GC。
CMS收集器提供了两个参数来解决这个问题。
1:-XX:+UseCMSCompactAtFullCollection.用于在Full GC后进行一次碎片整理,但是无法并发,会造成停顿时间过长。
2:-XX:CMSFullGCsBeforeCompaction.这个参数用于设置在执行多少次压缩的Full GC后,跟着来一次带压缩的。

垃圾回收器组合的启用参数:
UseSerialGC:虚拟机运行在Client模式下的默认值,打开此开关后,使用Serial+Serial Old的收集器组合进行内存回收。
UseParNewGC:打开此开关后,使用ParNew+Serial Old的收集器组合进行内存回收。
UseConcMarkSweepGC:打开此开关后,使用ParNew+CMS+Serial Old的收集器组合进行内存回收。
UseParallelGC:虚拟机运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge+Serial Old(PS MarkSweep)的收集器组合进行内存回收。
UseParallelOldGC:打开此开关后,使用Parallel Scavenge+Parallel Old的收集器组合进行内存回收。
综上所述对于垃圾回收器的选择,须根据CPU的个数或者逻辑核数来选择,
1个CPU用Serial+Serial Old的组合
2-3个CPU用Parallel Scavenge+Parallel Old的组合
4个或者4个以上CPU用ParNew+CMS+Serial Old的组合。

  • 大小: 40.6 KB
分享到:
评论

相关推荐

    Java内存管理原理.pdf

    Java内存管理原理.pdf

    Java内存管理原理及内存区域详解

     Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干不同的数据区域,这些区域都有各自的用途以及创建和销毁的时间。Java虚拟机所管理的内存将会包括以下几个运行时数据区域,如下图所示:  ...

    java内存管理的原理.docx

    java内存管理的原理

    深入Java核心 Java内存分配原理精讲

    Java内存分配与管理是Java的核心技术之一,今天我们深入Java核心,详细介绍一下Java在内存分配方面的知识。一般Java在内存分配时会涉及到以下区域:  ◆寄存器:我们在程序中无法控制  ◆栈:存放基本类型的...

    Java内存分配原理

    JAVA内存分配与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识

    java内存讲解

    内容包括:JVM的垃圾回收机制详解和调优;深入Java核心 Java内存分配原理精讲;详细介绍Java的内存管理与内存泄露,三个文档整合

    Java虚拟机的内存管理

    Java虚拟机如何管理内存管理,帮助你更深刻了解Java虚拟机的内存管理原理

    操作系统 linux 请求分页 模拟内存管理实验报告java(内含源码)

    通过实现一个操作系统的内存管理的模拟系统,观察内存空闲分区管理、内存分配和回收过程,了解内存管理技术等特点,掌握内存管理中的分配、回收和置换算法,加深对请求调页系统的原理和实现过程的理解。

    最详细的java内存讲解

    JVM的垃圾回收机制详解和调优; 深入Java核心 Java内存分配原理精讲; 详细介绍Java的内存管理与内存泄露;

    C++内存管理的原理和说明

    内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,因此要想成为C++高手,内存管理一关是必须要过的,除非放弃C++,转到Java或者.NET,他们的内存管理基本是自动的,当然你也放弃了自由和对内存的支配权,...

    opencvmem:OpenCV Java内存管理

    这是一个NetBeans 8 Java项目,展示了... 如果您只是想了解OpenCV Java内存管理的工作原理,请跳过以下项目。 已安装Java(我使用了Oracle JDK 8,Ubuntu,X86_64) 已安装Ant(用于OpenCV构建) 您需要安装OpenCV依赖

    JVM工作原理及内存管理机制

    在执行方法时JVM提供了invokestatic、invokevirtual、invokeinterface和invokespecial四种指令来执行 ...(4)invokespecial:JVM对于初始化对象(Java构造器的方法为:)以及调用对象实例中的私有方法时。

    01-JavaSE-JAVA运行原理.ppt

    简单性:摒弃了C++中易引发错误的特性,如指针和内存管理; 面向对象性:支持代码继承及重用; 分布式:支持网络应用编程接口,并提供了相应的类库; 健壮性:强制型机制、异常处理、垃圾自动回收; 跨...

    操作系统仿真OS内核 作业管理、内存管理 、虚存管理 进程调度等功能JAVA项目文件以及报告、PPT演示、视频演示、测试报告。

    以计算机操作系统原理为指导,利用面向对象程序设计技术仿真 OS 内核的作业管理、 连续内存管理、页式虚存管理、进程同步与互斥、缓冲技术、磁盘管理和文件管理的 API 功 能,可视化显示操作系统工作过程,完成操作...

    现代编译原理 虎书java版

    “虎书”。虎书出版比较晚,与《编译原理》的知识点差不多,但增加了数据流分析、循环优化、内存管理等内容。与虎书比,《编译原理》更适合国内的编译原理课程教学。

    java源码包---java 源码 大量 实例

     基于JAVA的UDP服务器模型源代码,内含UDP服务器端模型和UDP客户端模型两个小程序,向JAVA初学者演示UDP C/S结构的原理。 简单聊天软件CS模式 2个目标文件 一个简单的CS模式的聊天软件,用socket实现,比较简单。 ...

    优秀的Java程序员必须了解GC的工作原理

    一个优秀的Java程序员必须了解GC的工作原理、如何优化GC的性能、如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统、实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的...

    java面试八股文2023完整版110题附带答案

    Java的垃圾回收机制是Java内存管理的一部分,它负责自动回收程序中不再使用的对象所占用的内存。垃圾回收器通过跟踪对象的引用来判断哪些对象不再被使用,当这些对象不再被引用时,垃圾回收器会自动回收这些对象的...

    java核心面试技术点

    特点:在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 java 内存模型 ( java memory model ):根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java...

    Objective-C内存管理教程和原理剖析

    Objective-C的内存管理机制与.Net/Java那种全自动的垃圾回收机制是不同的,它本质上还是C语言中的手动管理方式,只不过稍微加了一些自动方法。 1 Objective-C的对象生成于堆之上,生成之后,需要一个指针来指向它。 ...

Global site tag (gtag.js) - Google Analytics