面试题答案
一键面试Java堆内存与本地内存交互
- 参数传递:
- 当Java调用Native方法时,Java堆内存中的对象作为参数传递给本地代码。例如,如果Java方法有一个字符串参数,这个字符串对象在Java堆中,会以某种方式(如通过JNI传递字符串的指针和长度等信息)传递给本地代码。本地代码可以访问这些数据,但它并不直接操作Java堆内存,而是通过JNI提供的接口间接操作。
- 基本数据类型(如int、long等)则直接从Java栈传递到本地栈,不存在堆内存交互问题。
- 返回值:
- Native方法返回的数据,如果是基本数据类型,直接从本地栈传递回Java栈。对于对象类型,本地代码可能创建一个新的对象,并通过JNI将其引用返回给Java代码,然后Java代码可以将该引用存储在Java堆中的对象引用变量中。例如,本地代码可能创建一个新的Java字符串对象并返回其引用,Java代码可以将这个引用赋值给堆中的字符串引用变量。
- 本地代码内存分配:本地代码可以在本地内存(非Java堆内存)中分配内存。例如,使用C语言的
malloc
函数。这些内存与Java堆内存是相互独立的。然而,Java代码可以通过JNI来访问这些本地分配的内存,但需要遵循JNI的规则。例如,Java代码可以获取本地内存的指针,并通过JNI函数来操作这块内存。
Java垃圾回收机制对Native方法涉及内存的影响
- 本地内存不直接受Java垃圾回收管理:Java垃圾回收机制主要管理Java堆内存中的对象。本地代码分配的内存(如使用
malloc
等函数在C语言中分配的内存)并不在Java堆内,所以Java垃圾回收器不会自动回收这些内存。如果本地代码没有正确释放这些内存,就会导致内存泄漏。 - JNI引用与垃圾回收:JNI提供了不同类型的引用(如局部引用、全局引用和弱全局引用)。局部引用在本地方法返回时会自动释放,这意味着Java垃圾回收器可以正常管理与该局部引用相关的Java对象。全局引用和弱全局引用则需要开发者手动释放,如果不释放,即使Java对象在堆中不再被其他Java代码引用,由于存在全局引用,垃圾回收器也不会回收该对象,可能导致内存泄漏。同时,对于弱全局引用,垃圾回收器可能在适当的时候回收对象,即使存在弱全局引用,这取决于对象的可达性分析结果。
- Finalize方法与本地内存清理:Java对象可以重写
finalize
方法,在对象被垃圾回收之前执行清理操作。这可以用来释放与Java对象关联的本地内存。例如,在finalize
方法中调用JNI函数来释放本地代码分配的内存。然而,finalize
方法的调用时机不确定,并且从Java 9开始,finalize
方法被标记为deprecated,不推荐使用。更好的方式是使用AutoCloseable
接口和try - with - resources
语句,在对象使用完毕后及时释放资源。