`
扫地僧
  • 浏览: 29922 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Java引用详解

 
阅读更多

一、对象可达性

Java虚拟机有5个不同级别的对象的可达性。

●强可达(Strongly reachable

如果一个对象可以被一些线程直接使用而不用通过其他引用对象(reference objects),那么它就是强可达。一个新创建的对象对创建它的线程来讲就是强可达的。

这是我们知道并且一直在使用的引用类型(译注:通常被new出来的对象都是强可达的,他们的引用就是强引用)。任何通过强引用所使用的对象(在一个活动线程中)都不会被GC回收。

●软可达(Softly reachable

如果一个对象没有强可达性,但是它可以通过一个软引用(soft reference)来使用,那么它就具有软可达性。

只有当系统需要更多内存时,GC才会回收具有软可达性的对象。在内存不足前,GC保证一定回收软可达的对象。

有可能我们会在代码中写下这么几行:“嘿,我想要把一些数据保存在内存中。但只要JVM快把内存用光的时候,就可以直接将这些东西回收并将这些引用置为null。我会在代码里面处理这种情况。”关于软引用(SoftReference)何时应该被回收的算法依赖于不同的JVM发行版本。它往往是一个跟引用(reference)的使用频率和使用间隔有关的函数。

软引用可用来实现内存敏感的高速缓存.但是具体的行为还是得依赖于JVM。并且多少跟内存回收机制有关,保障很少并且跟具体的JVM发行版本有关。为了缓存的可靠(及其他更多特性),大多数人都会选用像Ehcache而不是用软引用实现自己的缓存。但在一些场合,使用软引用确实可以让代码非常优雅、简洁。

●弱可达(Weakly reachable

如果一个对象既没有强可达性,也没有软可达性,但是它可以通过一个弱引用(weak reference)来使用,那么他就具有弱可达性。当弱引用指向的弱可达对象没有其他的引用,那么这个对象就会被回收。

弱引用不能阻止垃圾回收机制清理他指向的引用。弱引用最常见的使用情景是通过WeakHashMap。它是一种简单地将对象的生命周期跟Map中对象的索引域(key)绑定的方式。只有当WeakHashMap中的Key是强可达,也就是WeakHashMap中的数据域(Data域)的对象,在应用程序的其他地方有别的引用的时候,它里面的值才不会被回收。一旦应用程序中没有其他对WeakHashMap中对象的引用,那么它的所有的key就会变成弱可达,不需要用户的额外干预,所有WeakHashMap中的对象都会被清除。这是一种优雅地防止内存泄露的方式。

●虚可达(Phantom reachable

如果一个对象既没有强可达性,也没有软可达性、弱可达性,他已经被终结(finalized),并且有一些虚引用(phantom reference)指向它,那么它就具有虚可达性。

虚引用(PhantomReference)指向的对象是不能被取回使用的。它的get()方法永远返回null,所以它有什么用呢?

所有的引用类型都允许在构造函数中指定一个引用队列(ReferenceQueue)。从语义上讲一个虚引用(PhantomReference)以什么方式、何时入队让对象终结(finalization)以一种更好、更健壮的方式进行。

●不可达(Unreachable

当一个对象不能通过以上的方式指向,那么这个对象就变得不可达,并因此适合被回收。

二、可达性分析算法

在主流的商用程序语言(Java、C#,甚至包括前面提到的古老的Lisp)的主流实现中,都是称通过可达性分析(Reachability Analysis)来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。如图3-1所示,对象object 5、object 6、object 7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。


 

在Java语言中,可作为GC Roots的对象包括下面几种:

虚拟机栈(栈帧中的本地变量表)中引用的对象。

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

方法区中常量引用的对象。

本地方法栈中JNI(即一般说的Native方法)引用的对象。

三、引用的概念及应用

在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

●强引用

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

●软引用(SoftReference

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。 

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

SoftReference的特点是它的一个实例保存对一个Java对象的软引用,该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。另外,一旦垃圾线程回收该Java对象之后,get()方法将返回null。 

为什么要使用SoftReference?在不使用SoftReference的时候,程序所需要的数据要么保存在内存中,如果数据量比较大的话,会占用比较多的内存;要么保存在介质中,需要时进行加载,在性能上肯定比不上直接读取内存。SoftReference算是比较折中的一种解决方案,它先将数据保存在内存中,在内存不足的情况下,才将数据回收。软引用可用来实现内存敏感的高速缓存.但是具体的行为还是得依赖于JVM。并且多少跟内存回收机制有关,保障很少并且跟具体的JVM发行版本有关。为了缓存的可靠(及其他更多特性),大多数人都会选用像Ehcache而不是用软引用实现自己的缓存。

 示例:

大数据对象:照片Image

/**
 * 图片,包含大量数据
 * @author Grucee
 */
public class Image {
	private byte[][] buffer;
	private int id;	
	public Image(int id, byte[][] data) {
		this.id = id;
		buffer = data;
	}	
	public int getId() {
		return this.id;
	}	
	public String toString() {
		return "image[" + this.id + "]";
	}
}

 缓存管理ImageCacheManager

/**
 * 图片缓存
 * 
 * @author Grucee
 */
public class ImageCacheManager {
	private static ImageCacheManager imageCache = null;
	private ImageCacheManager() {}
	/**
	 * 作为一个管理器,当然要单例了,不允许随便new一个就当做管理器
	 * @return
	 */
	public static ImageCacheManager instance() {
		if (imageCache == null) {
			synchronized (ImageCacheManager.class) {
				if (imageCache == null) {
					imageCache = new ImageCacheManager();
				}
			}
		}

		return imageCache;
	}

	// 保存数据的地方
	private Map<Integer, SoftReference<Image>> cache = new HashMap<Integer, SoftReference<Image>>();

	public void put(Image image) {
		cache.put(image.getId(), new SoftReference<Image>(image));
	}

	private Image loadImage(int id) {
		byte[][] data = new byte[1024][1024];
		data[1023][1023] = 1;
		return new Image(id, data);
	}

	public Image get(int id) {
		SoftReference<Image> ref = cache.get(id);
		// 没有放入过缓存,第一次加载
		if (ref == null) {
			Image loadFirst = loadImage(id);
			// 放入缓存
			put(loadFirst);
			return loadFirst;
		}

		// 下面的情况是该图片已经被加载过,但是可能由于内存不足,又被回收了
		// 为了便于理解,使用了if else分支方式,其实else不是必须的。
		// 这两个分支,不管哪一个返回的都是对image的强引用
		Image cachedImage = ref.get();
		if (cachedImage == null) {
			// 从真实介质中读取(此处模拟这个操作)
			Image imageGetFromMedia = loadImage(id);
			// 缓存起来
			put(imageGetFromMedia);

			System.out.println("get image:" + id + " from media.");
			return imageGetFromMedia;
		} else {
			System.out.println("get image:" + id + " from cache.");
			return cachedImage;
		}
	}
}

 测试类

/**
 * 测试类
 * @author Grucee
 */
public class SoftReferenceTest 
{
	private ImageCacheManager cacheMgr = ImageCacheManager.instance();
	/**
	 * 设置启动参数
	 * @param args
	 */
	public static void main(String[] args) {
		SoftReferenceTest tester = new SoftReferenceTest();
		//先把数据load进来
		for (int i = 0; i < 10; i++) {
			tester.cacheMgr.get(i);
		}
		
		//一张特殊的图片,这里保存了一个对这张图片的强引用,所以该对象不会被回收
		Image myPhoto = tester.cacheMgr.get(7);
		System.out.println("--------------------------");
		System.out.println(myPhoto);
		System.out.println("--------------------------");
		
		//打印图片
		for (int i = 0; i < 10; i++) {
			tester.printImage(i);
		}
	}
	
	public void printImage(int id) {
		System.out.println(cacheMgr.get(id));
	}
}

 启动设置内存堆大小为10M

-Xmx10M -Xms10M -verbose:gc

测试一:

当测试类SoftReferenceTest中特殊图片是第8张是,运行结果

[GC (Allocation Failure)  2048K->1972K(9728K), 0.0048794 secs]

get image:8from cache.

--------------------------

image[8]

--------------------------

[GC (Allocation Failure)  2898K->3066K(9728K), 0.0019205 secs]

get image:0from media.

image[0]

get image:1from media.

image[1]

[GC (Allocation Failure)  5114K->5338K(9728K), 0.0007850 secs]

[Full GC (Ergonomics)  5338K->4866K(9728K), 0.0032548 secs]

get image:2from media.

image[2]

get image:3from media.

image[3]

[GC (Allocation Failure)  6914K->7114K(8704K), 0.0006606 secs]

[Full GC (Ergonomics)  7114K->6907K(8704K), 0.0028911 secs]

get image:4from media.

image[4]

[Full GC (Ergonomics)  7931K->7912K(8704K), 0.0075340 secs]

get image:5from media.

image[5]

[GC (Allocation Failure)  3014K->3022K(9216K), 0.0004105 secs]

get image:6from media.

image[6]

[GC (Allocation Failure)  4045K->4118K(9216K), 0.0011056 secs]

get image:7from media.

image[7]

get image:8from cache.

image[8]

[GC (Allocation Failure)  5141K->5262K(9216K), 0.0005411 secs]

get image:9from media.

image[9]

 

测试二:

当测试类SoftReferenceTest中特殊图片是第7张是,运行结果

[GC (Allocation Failure)  2048K->1972K(9728K), 0.0048794 secs]

get image:7from media.

--------------------------

image[7]

--------------------------

[GC (Allocation Failure)  2898K->3066K(9728K), 0.0019205 secs]

get image:0from media.

image[0]

get image:1from media.

image[1]

[GC (Allocation Failure)  5114K->5338K(9728K), 0.0007850 secs]

[Full GC (Ergonomics)  5338K->4866K(9728K), 0.0032548 secs]

get image:2from media.

image[2]

get image:3from media.

image[3]

[GC (Allocation Failure)  6914K->7114K(8704K), 0.0006606 secs]

[Full GC (Ergonomics)  7114K->6907K(8704K), 0.0028911 secs]

get image:4from media.

image[4]

[Full GC (Ergonomics)  7931K->7912K(8704K), 0.0075340 secs]

get image:5from media.

image[5]

[GC (Allocation Failure)  3014K->3022K(9216K), 0.0004105 secs]

get image:6from media.

image[6]

[GC (Allocation Failure)  4045K->4118K(9216K), 0.0011056 secs]

get image:7from cache.

image[7]

get image:8from media.

image[8]

[GC (Allocation Failure)  5141K->5262K(9216K), 0.0005411 secs]

get image:9from media.

image[9]

测试一和测试二对比

首先我们通过启动参数-Xmx10M -Xms10M -verbose:gc,将jvm的堆内存大小设置为10M。

现在我们有10张照片,每张1M大小。由于JVM堆中还有方法区(存放类定义)、常量池等,所以这10张照片是不能够全部存放在cache中的。

通过上面测试一和测试二的对比我们可以看出,当全部加载10张图片后,我们获取第八张图片是从cache中取的;获取第七张图片时,是重新加载的(从media中加载)。所以实际上,由于内存有限,我们的cache只是在内存中保存了2张照片;之前加载的照片由被回收了,通过[GC (Allocation Failure)  6914K->7114K(8704K), 0.0006606 secs]可以看出进行了内存回收。

所以,我们这里实现的cache在内存不足的时候,会自动被回收的。

之后我们强引用了一张图片,可以看出存在强引用的这张图片是一直存在内存中的,不会被回收。

   

●弱引用(WeakReference

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

弱引用最常见的用处是在集合类中,尤其在哈希表中。哈希表的接口允许使用任何Java对象作为键来使用。当一个键值对被放入到哈希表中之后,哈希表对象本身就有了对这些键和值对象的引用。如果这种引用是强引用的话,那么只要哈希表对象本身还存活,其中所包含的键和值对象是不会被回收的。如果某个存活时间很长的哈希表中包含的键值对很多,最终就有可能消耗掉JVM中全部的内存。

对于这种情况的解决办法就是使用弱引用来引用这些对象,这样哈希表中的键和值对象都能被垃圾回收。Java中提供了WeakHashMap来满足这一常见需求。

示例:

public staticvoid main(String[] args)

   {

      Map<Integer, String> map = new WeakHashMap<Integer, String>();

      Integer key = new Integer(1);

      map.put(key, "test");

     

      // key不再有强引用

      key = null;

      System.gc();

     

      //等待一段时间,进行垃圾回收

      try {

         Thread.sleep(10000);

      } catch (InterruptedException e) {

         e.printStackTrace();

      }

     

      System.out.println(map.size());

   }

输出:

[GC (Allocation Failure)  509K->520K(130560K), 0.0028151 secs]

[GC (System.gc())  746K->560K(131072K), 0.0007948 secs]

[Full GC (System.gc())  560K->513K(131072K), 0.0091938 secs]

0

可见当WeakHashMapkey不存在强引用时,随时都会从map中移除。  

●虚引用(PhantomReference

1 基本概念

虚引用顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。 

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。 

ReferenceQueue queue = new ReferenceQueue ();                      

PhantomReference pr = new PhantomReference (object, queue);

程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。 

2、析构finalization

Java中内存管理是自动进行的。开发者无需关心已经释放的对象的内存回收。这样的一个缺点是开发者无法得知某个特定对象何时被回收;另外,开发者无法控制内存管理。不过,java.lang.ref包定义了一些可以和垃圾回收器进行有限交互的类。具体的类是SoftReference,WeakReference以及PhantomReference,它们是Reference的子类,以不同的方式和垃圾回收器交互。

有时需要在对象被回收前做一些清理工作,可以使用析构方法。该特性可以用来回收对象相关的本地资源。但是,Java中的析构方法有很多的问题。

第一:我们无法预期finalize()何时会被调用。没有任何保证某个对象一定会被垃圾回收。一个在JVM整个生命周期中可达的对象永远不会被回收;也有可能,在对象变成可回收之后和JVM停止之前,垃圾回收线程没有运行。

第二:Java析构方法会将应用程序拖慢。管理重载了finalize()方法的对象需要耗费JVM更多的资源。

第三:对象即使被调用了finalize()方法,也无法保证该对象一定会被回收;并且,可以在finalize()方法中对对象进行拯救(重新赋予强引用)。

虽然有可达性分析算法来判定对对象状态,但这并不是对象是否被回收的条件,对象回收的条件远远比这个复杂,比如无法通过ROOT找到的对象,也不一定会回收,会进入一个死缓的阶段,那些无法通过根节点引用链找到的对象,会被第一次标记,并进行一次筛选,筛选条件是此对象是否有必要执行finalize()方法,当对象没有覆盖finalize()方法,或者finalize() 方法已经被虚拟机调用过,虚拟机都视为没必要执行

如果该对象被判定为有必要执行finalize(),那么对象会被放置在一个F-Queue 的队列中,并由一个优先级较低的Finalizer线程去执行,这里的执行是 JVM 会触发这个方法,但并不保证等待他运行结束,因为finalize() 方法执行慢,或者死循环,会影响该队列其他元素执行。

执行finalize() 方法就会进行第二次标记,然后等待JVM 进行回收了,而在finalize() 方法执行的同时,可以对对象进行拯救,也就是说在执行方法内部,再次对对象进行引用,那么对象就复活了。

示例:
public class SaveFinalizedObject {
	private static class User {
		public static Resource res = null;
	}
	private static class Resource {
		@Override
		protected void finalize() throws Throwable {
			super.finalize();
			System.out.println("执行finalize方法,并拯救对象");
			User.res = this;
		}
	}
	public static void main(String[] args) throws InterruptedException {
		// 使用者占用资源
		User.res = new Resource();
		// 使用者释放资源,这时上面new的Resource对象已经没有强引用,可以被垃圾回收
		User.res = null;

		// 此处如果执行了finalize方法后,对象即可被拯救成功,又有了强引用
		System.gc();
		Thread.sleep(500); // gc线程优先级比较低,此处等待垃圾回收
		System.out.println(User.res);

		// 再次释放资源,让资源被回收(此时已经不能被拯救,因为finalize方法只会被调用一次)
		User.res = null;
		Thread.sleep(500);
		System.out.println(User.res);
	}
}
 程序输出:

 

[GC (Allocation Failure)  509K->504K(130560K), 0.0008240 secs]

 

[GC (System.gc())  739K->568K(131072K), 0.0019017 secs]

 

[Full GC (System.gc())  568K->513K(131072K), 0.0067556 secs]

 

执行finalize方法,并拯救对象

 

grucee.test.Main$Resource@15db9742

 

null

 

强烈建议不要重写Objectfinalize()方法。

 

3、虚引用何时使用虚引用?

 

当理智告诉我们不应该使用finalize(由于上面一系列的缺点),可以考虑使用虚引用。该类型的引用和java.lang.ref包中定义的其他引用不同,因为它不是用来访问对象的,只是作为一个对象已经被析构、垃圾回收期准备回收它的内存的信号。

 

正如API文档中所说:

 

虚引用对象,在垃圾回收器确定它所引用的对象要被回收之后,会被放入队列。虚引用常被用来以比JAVA析构机制更灵活的方式执行清理动作。

虚引用是一种比较安全的知道对象已经从内存中移除的方式。例如,考虑一个处理大图片的应用程序。我们希望内存中已经存在的大图片在被垃圾回收期准备回收后,才去加载新的大图片。这种情况下,虚引用是一种灵活、安全的选择。旧的图片对象在析构后,会被放在ReferenceQueue引用队列中;在队列中可获取该引用后,我们就可以加载新的大图片到内存中了。

示例:

 

/**
 * 虚引用测试用例
 * @author Grucee
 */
public class PhantomReferenceTest {

	private static class Connection {
		private int resourceId;
		private Connection(int resourceId) {
			System.out.println("分配资源:" + resourceId);
			this.resourceId = resourceId;
		}
			
		public int getResourceId() {
			return this.resourceId;
		}
	}
	
	private static class ConnectionMonitor extends PhantomReference<Connection> {
		private int resourceId;
		private ReferenceQueue<Connection> queue;
		public ConnectionMonitor(Connection referent, ReferenceQueue<Connection> queue) {
			super(referent, queue);
			this.resourceId = referent.getResourceId();
			this.queue = queue;
		}
		
		public void waitToCleanUP() {
			try {
				//此处会阻塞一直到被引用的connection被回收
				queue.remove();
				this.clear();
				//资源清理
				System.out.println("清理资源:" + this.resourceId);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 上个连接的资源回收之后,才允许分配下一个连接
	 * @author Grucee
	 */
	public static class ConnectioinManager {
		private static ConnectionMonitor monitor = null;
		private ConnectioinManager(){}
		public synchronized static Connection openConnection(int resourceId) {
			if (monitor != null) {
				monitor.waitToCleanUP();
			}
			
			Connection conn = new Connection(resourceId);
			ReferenceQueue<Connection> queue = new ReferenceQueue<Connection>();
			monitor = new ConnectionMonitor(conn, queue);
			return conn;
		}
	}
	
	public static void main(String[] args) {
		Connection conn = ConnectioinManager.openConnection(101);
		//如果不添加这一行,会发现一直得不到102资源
		conn = null;
		System.gc();
		conn = ConnectioinManager.openConnection(102);
	}
}

 

分享到:
评论

相关推荐

    java堆栈详解.pdf

    在函数中定义的 一些基本类型的变量和对象的引用变量都是在函数 的栈内存中分配。当在一段代码块中定义一个变量时,java 就在栈中 为这个变量分配内存空间,当超过变量的作用域后,java 会自动释放 掉为该变量...

    JVM内幕:java虚拟机详解

    ### JVM内幕:java虚拟机详解 #### 一、概述 Java虚拟机(JVM)是运行Java应用程序的核心组件,它提供了一个可移植、安全且高性能的环境。本文将深入探讨JVM的内部架构及其各个组成部分的功能。 #### 二、Java虚拟机...

    Java集合详解,详细讲解java的集合类

    Java集合框架是Java编程语言中的核心部分,它提供了一种高效、灵活的方式来组织和操作对象的集合。在Java中,集合主要分为两大类:Collection和Map。本文将深入讲解Java集合类,特别是Collection接口和其下的List、...

    java基础-java多态详解

    Java 多态详解 Java 多态是 Java 编程语言中的一种基本概念,它允许开发者定义一个接口,并且可以通过不同的类来实现该接口。多态性是 Java 面向对象编程的核心机制之一,它使得程序更加灵活、可维护和可扩展。 ...

    Java中的引用类型详解:强引用、软引用、弱引用与虚引用

    在Java中,对象的引用方式对于内存管理和对象生命周期的控制至关重要。从JDK 1.2版本开始,Java引入了四种不同级别的引用:强引用(Strong Reference)、软...希望本文能够为您提供在Java引用类型的使用上清晰的指导

    详解java堆和栈

    ### 详解Java堆和栈 #### 一、引言 在Java编程中,理解堆(Heap)和栈(Stack)的概念及其区别对于程序员来说至关重要。本文将深入剖析这两个概念,并探讨它们之间的差异以及如何影响程序的运行。 #### 二、Java...

    Java内存模型详解

    ### Java内存模型详解 #### 1. JMM简介 ##### i. 内存模型概述 Java内存模型(Java Memory Model, JMM)是Java虚拟机(JVM)的一部分,用于规定程序中的各种变量(包括实例字段、静态字段和数组元素等)在多个...

    java 程序详解

    【Java程序详解】 Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems(后被Oracle公司收购)于1995年推出。它的设计目标是具有跨平台性、可移植性、安全性和高效性,使得开发者可以编写一次代码,到处...

    Java Bug模式详解 pdf版

    7. **内存泄漏**:Java的垃圾回收机制虽然可以自动管理内存,但不当的引用可能导致内存泄漏。书中会讲解如何检测和避免内存泄漏,使用内存分析工具进行诊断。 8. **集合类误用**:如ArrayList、HashMap等集合类的...

    java语法详解新手必备_java语法详解_

    Java 8引入了更多的新特性,如Lambda表达式、方法引用来简化代码,Stream API以更直观的方式处理集合,以及日期时间API的改进等,这些都是进一步提升编程效率的关键。学习Java的过程中,理解并熟练应用这些知识点,...

    java中ThreadLocal详解

    ### Java中ThreadLocal详解 #### 一、ThreadLocal概述 在Java多线程编程中,`ThreadLocal`是一个非常重要的工具类,它提供了一种在每个线程内部存储线程私有实例的方法。通常情况下,当多个线程共享某个变量时,...

    core java 详解

    变量是用来存储数据的容器,Java提供了基本数据类型(如int、float、char等)以及引用数据类型。运算符用于执行计算或改变变量值,而控制流(if语句、for循环、while循环)则决定了程序的执行顺序。 接着,文档会...

    Java栈详解Java栈详解.doc

    - 静态方法没有`this`引用,非静态方法的第一项是`this`引用,指向调用该方法的对象实例。 - 参数和局部变量按照声明的顺序存储在局部变量区,例如,方法参数和局部变量的存储结构可以通过图表清晰地展示。 4. **...

    jvm详解(java虚拟机详解)

    ### jvm详解(java虚拟机详解) #### Java与JVM概览 Java作为一种广泛使用的编程语言,其核心优势之一便是“一次编写,到处运行”的特性,这背后的关键技术支撑即为Java虚拟机(JVM)。JVM是一种抽象计算模型,允许...

    java堆栈详解 PDF 下载

    Java 堆栈详解 Java 是一种广泛使用的面向对象的编程语言,它的内存管理机制是其强大特性的关键部分。在 Java 中,内存分为两个主要区域:堆(Heap)和栈(Stack)。本文将深入探讨Java堆栈的概念、工作原理以及...

    java 反射机制详解

    这展示了如何在不直接引用具体类名的情况下,通过反射动态地操作对象。 反射的核心类有以下这些: 1. **Class 类**:代表 Java 类的运行时信息,可以获取类的所有属性、方法、构造器等信息。 2. **Constructor 类*...

    Java面试题答案详解.rar

    2. 变量与数据类型:Java支持基本数据类型(如int、double)和引用数据类型(如类、接口、数组)。理解它们的区别和用法是必要的。 3. 控制结构:包括条件语句(if-else、switch-case)和循环(for、while、do-while...

    java反射机制详解

    ### Java反射机制详解 #### 一、反射机制是什么 反射机制是Java编程语言的一个核心特性,它允许程序在运行时动态地获取类的信息,并且能够动态地创建对象和调用对象的方法。简单来说,反射机制使得Java程序可以...

Global site tag (gtag.js) - Google Analytics