Android面试题|Java进阶之深入理解对象与垃圾回收机制常见面试题

内存泄漏和内存溢出辨析 内存溢出:实实在在的内存空间不足导致;
内存泄漏:该释放的对象没有释放,多见于自己使用容器保存元素的情况下。
常量池与String 常量池 常量池有很多说法,包括运行时常量池、class文件常量池、字符串常量池。
虚拟机规范只规定以上区域属于方法区,并没有规定虚拟机厂商的实现。
严格来说常量池只分为静态常量池和运行时常量池。JDK1.7版本之后,运行时常量池转移到堆内存中了,这里指的是物理空间,而逻辑上还是属于方法区(方法区是逻辑分区)。
一些概念:
字面量:
给基本类型变量赋值的方式就叫做字面量或者字面值
比如:int i=120; long j=10L;
符号引用:包括类和方法的全限定名(例如 String 这个类,它的全限定名就是 Java/lang/String,所以Java/lang/String就是一个符号引用)、字段的名称和描述符以及方法的名称和描述符。
直接引用:具体对象的索引值。
静态常量池
静态常量池是指class文件中的常量池,用于存放字符串字面量、符号引用以及类和方法的信息。
可以使用javap命令查看下面java代码对应的字节码中的常量池

/** * @author King老师 * 栈帧执行对内存区域的影响 */ public class Person {public int work() throws Exception {//运行过程中,打包一个栈帧 int x = 1; //x是一个局部变量 int y = 2; int z = (x + y) * 10; return z; }public static void main(String[] args) throws Exception { Person person = new Person(); //person是一个引用,存放在栈中,new Person()是创建一个对象,存放在堆中 person.work(); //执行完了,出栈 person.hashCode(); int i = 12; } }

进入 Person.class所在目录,执行:javap -v Person.class
Last modified 2020年8月20日; size 731 bytes SHA-256 checksum 326bd51b63d835ab4e480d26c8345b11db4d260c0da6a5a8770f3f6ca2867e28 Compiled from "Person.java" public class com.example.javaadvanced.jvm.ex1.Person minor version: 0 major version: 51 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #2// com/example/javaadvanced/jvm/ex1/Person super_class: #6// java/lang/Object interfaces: 0, fields: 0, methods: 3, attributes: 1 Constant pool: #1 = Methodref#6.#30// java/lang/Object."":()V #2 = Class#31// com/example/javaadvanced/jvm/ex1/Person #3 = Methodref#2.#30// com/example/javaadvanced/jvm/ex1/Person."":()V #4 = Methodref#2.#32// com/example/javaadvanced/jvm/ex1/Person.work:()I #5 = Methodref#6.#33// java/lang/Object.hashCode:()I #6 = Class#34// java/lang/Object #7 = Utf8 #8 = Utf8()V #9 = Utf8Code #10 = Utf8LineNumberTable #11 = Utf8LocalVariableTable #12 = Utf8this #13 = Utf8Lcom/example/javaadvanced/jvm/ex1/Person; #14 = Utf8work #15 = Utf8()I #16 = Utf8x #17 = Utf8I #18 = Utf8y #19 = Utf8z #20 = Utf8Exceptions #21 = Class#35// java/lang/Exception #22 = Utf8main #23 = Utf8([Ljava/lang/String; )V #24 = Utf8args #25 = Utf8[Ljava/lang/String; #26 = Utf8person #27 = Utf8i #28 = Utf8SourceFile #29 = Utf8Person.java #30 = NameAndType#7:#8// "":()V #31 = Utf8com/example/javaadvanced/jvm/ex1/Person #32 = NameAndType#14:#15// work:()I #33 = NameAndType#36:#15// hashCode:()I #34 = Utf8java/lang/Object #35 = Utf8java/lang/Exception #36 = Utf8hashCode { public com.example.javaadvanced.jvm.ex1.Person(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1// Method java/lang/Object."":()V 4: return LineNumberTable: line 7: 0 LocalVariableTable: StartLengthSlotNameSignature 050thisLcom/example/javaadvanced/jvm/ex1/Person; public int work() throws java.lang.Exception; descriptor: ()I flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=4, args_size=1 0: iconst_1 1: istore_1 2: iconst_2 3: istore_2 4: iload_1 5: iload_2 6: iadd 7: bipush10 9: imul 10: istore_3 11: iload_3 12: ireturn LineNumberTable: line 10: 0 line 11: 2 line 12: 4 line 13: 11 LocalVariableTable: StartLengthSlotNameSignature 0130thisLcom/example/javaadvanced/jvm/ex1/Person; 2111xI 492yI 1123zI Exceptions: throws java.lang.Exceptionpublic static void main(java.lang.String[]) throws java.lang.Exception; descriptor: ([Ljava/lang/String; )V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: new#2// class com/example/javaadvanced/jvm/ex1/Person 3: dup 4: invokespecial #3// Method "":()V 7: astore_1 8: aload_1 9: invokevirtual #4// Method work:()I 12: pop 13: aload_1 14: invokevirtual #5// Method java/lang/Object.hashCode:()I 17: pop 18: bipush12 20: istore_2 21: return LineNumberTable: line 17: 0 line 18: 8 line 19: 13 line 20: 18 line 21: 21 LocalVariableTable: StartLengthSlotNameSignature 0220args[Ljava/lang/String; 8141 personLcom/example/javaadvanced/jvm/ex1/Person; 2112iI Exceptions: throws java.lang.Exception } SourceFile: "Person.java"

上面信息中的Constant pool:里面的内容就是静态常量池中的数据。
运行时常量池
而运行时常量池存放的是运行时的直接引用和字符串常量池。
在类加载完成之后,将静态常量池中的符号引用替换成直接引用放入运行时常量池,将静态常量池中的字符串放入运行时常量池。
String 对象是如何实现的? 重要其实还是String。
了解了 String 对象的实现后,你有没有发现在实现代码中 String 类被 final 关键字修饰了,而且变量 char 数组也被 final 修饰了。我们知道类被 final 修饰代表该类不可继承,而 char[]被 final+private 修饰,代表了 String 对象不可被更改。Java 实现的这个特性叫作 String 对象的不可变性,即 String 对象一旦创建成功,就不能再对它进行改变。
Java中创建字符串对象的两种方式:
一种是通过字符串常量的方式创建,如 String str=“abc”;
这种方式,首先在代码编译时,字符串常量“abc”将会放入class文件的静态常量池中,在类加载时,“abc"将会在运行时常量池中创建;
Android面试题|Java进阶之深入理解对象与垃圾回收机制常见面试题
文章图片

运行时返回常量池中的字符串对象的引用。
这种方式可以减少同一个值的字符串对象的重复创建,节约内存。
另一种是字符串变量通过 new 形式的创建,如 String str = new String(“abc”)
这种方式,首先在代码编译时,字符串常量"abc"将会放入class文件的静态常量池中,在类加载时,“abc"将会在运行时常量池中创建;
Android面试题|Java进阶之深入理解对象与垃圾回收机制常见面试题
文章图片

其次,在调用 new 时,JVM 命令将会在堆内存中创建一个 String 对象,调用 String 的构造函数,String对象的char数组会引用常量池中的"abc” 字符串对象;最后,str 将引用堆中创建的 String 对象。Android面试题|Java进阶之深入理解对象与垃圾回收机制常见面试题
文章图片

String 的 intern 方法
如果调用String 的 intern 方法,会去查看字符串常量池中是否有等于该字符串对象的字符串(通过 equals(Object)方法判断),如果没有,则会把该字符串对象添加到常量池中,并返回常量池中该字符串对象的引用;如果有,就返回常量池中的字符串对象的引用。(这个版本都是基于JDK1.7及以后版本)
代码实战
public class Location {private String city; private String region; public static void testStringCreate() {Location location = new Location(); location.setCity("深圳"); //"深圳"和"南山"两个字符串在常量池中创建 location.setRegion("南山"); //JVM首先会检查该对象是否在字符串常量池中,如果在,就返回该对象引用,否则新的字符串将在常量池中被创建。 //这种方式可以减少同一个值的字符串对象的重复创建,节约内存。 String str = "abc"; //首先在编译类文件时,"abcd"常量字符串将会放入到常量结构中,在类加载时,“abcd"将会在常量池中创建; //其次,在调用 new 时,JVM 命令将会调用 String 的构造函数,同时引用常量池中的"abcd” 字符串, // 在堆内存中创建一个 String 对象;最后,str 将引用 String 对象。 String str1 = new String("abcd"); }/** * String的intern()方法 */ public static void testStringIntern() { //new Sting() 会在堆内存中创建一个a的String对象, // “king"将会在常量池中创建 // 在调用intern方法之后,会去常量池中查找是否有等于该字符串对象的字符串(通过equals(Object)判断是否相等), // 有就返回常量池中该字符串对象的引用。 否则就将该字符串"king"加入常量池中,并返回该字符串的引用4 String a = new String("king").intern(); //调用 new Sting() 会在堆内存中创建一个b的String 对象,。 // 在调用intern方法之后,会去常量池中查找是否有等于该字符串对象的字符串(通过equals(Object)判断是否相等), // 有就返回常量池中该字符串对象的引用。 否则就将该字符串"king"加入常量池中,并返回该字符串的引用 String b = new String("king").intern(); //所以 a 和 b 引用的是同一个对象, 都是指string pool中的"king" if (a == b) { System.out.println("a==b"); } else { System.out.println("a!=b"); }}/** * 字符串拼接 * * String的+操作的实现原理 */ public static void testStringConcat() { String hello = "hello"; String hel = "hel"; String lo = "lo"; //+ 两边都是常量,编译时会在class常量池中放置拼接后的字符串"hello",类加载后放入运行时常量池, //hello1指向的是运行时常量池中的"hello"字符串对象。 String hello1 = "hel" + "lo"; System.out.println(hello == hello1); //true//+ 两边有一个不是常量 String hello2 = "hel" + lo; System.out.println(hello == hello2); //falseString hello3 = hel + lo; System.out.println(hello == hello3); //false System.out.println(hello2 == hello3); //false//常量池中只有"appleorange"字符串对象,没有"apple"字符串对象,也没有"orange"字符串对象 String str = "apple" + "orange"; }public static void main(String[] args) {testStringCreate(); testStringConcat(); testStringIntern(); }public String getCity() { return city; }public void setCity(String city) { this.city = city; }public String getRegion() { return region; }public void setRegion(String region) { this.region = region; } }

执行:javap -v Location.class
Last modified 2020年8月21日; size 2265 bytes SHA-256 checksum 177a9cc73d0d275d8f200e7104d1b12c6e404d32d7d52bee8f5095134225db58 Compiled from "Location.java" public class com.example.javaadvanced.jvm.ex2.Location minor version: 0 major version: 51 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #2// com/example/javaadvanced/jvm/ex2/Location super_class: #32// java/lang/Object interfaces: 0, fields: 2, methods: 9, attributes: 1 Constant pool: #1 = Methodref#32.#72// java/lang/Object."":()V #2 = Class#73// com/example/javaadvanced/jvm/ex2/Location #3 = Methodref#2.#72// com/example/javaadvanced/jvm/ex2/Location."":()V #4 = String#74// 深圳 #5 = Methodref#2.#75// com/example/javaadvanced/jvm/ex2/Location.setCity:(Ljava/lang/String; )V #6 = String#76// 南山 #7 = Methodref#2.#77// com/example/javaadvanced/jvm/ex2/Location.setRegion:(Ljava/lang/String; )V #8 = String#78// abc #9 = Class#79// java/lang/String #10 = String#80// abcd #11 = Methodref#9.#81// java/lang/String."":(Ljava/lang/String; )V #12 = String#82// king #13 = Methodref#9.#83// java/lang/String.intern:()Ljava/lang/String; #14 = Fieldref#84.#85// java/lang/System.out:Ljava/io/PrintStream; #15 = String#86// a==b #16 = Methodref#87.#88// java/io/PrintStream.println:(Ljava/lang/String; )V #17 = String#89// a!=b #18 = String#53// hello #19 = String#54// hel #20 = String#55// lo #21 = Methodref#87.#90// java/io/PrintStream.println:(Z)V #22 = Class#91// java/lang/StringBuilder #23 = Methodref#22.#72// java/lang/StringBuilder."":()V #24 = Methodref#22.#92// java/lang/StringBuilder.append:(Ljava/lang/String; )Ljava/lang/StringBuilder; #25 = Methodref#22.#93// java/lang/StringBuilder.toString:()Ljava/lang/String; #26 = String#94// appleorange #27 = Methodref#2.#95// com/example/javaadvanced/jvm/ex2/Location.testStringCreate:()V #28 = Methodref#2.#96// com/example/javaadvanced/jvm/ex2/Location.testStringConcat:()V #29 = Methodref#2.#97// com/example/javaadvanced/jvm/ex2/Location.testStringIntern:()V #30 = Fieldref#2.#98// com/example/javaadvanced/jvm/ex2/Location.city:Ljava/lang/String; #31 = Fieldref#2.#99// com/example/javaadvanced/jvm/ex2/Location.region:Ljava/lang/String; #32 = Class#100// java/lang/Object #33 = Utf8city #34 = Utf8Ljava/lang/String; #35 = Utf8region #36 = Utf8 #37 = Utf8()V #38 = Utf8Code #39 = Utf8LineNumberTable #40 = Utf8LocalVariableTable #41 = Utf8this #42 = Utf8Lcom/example/javaadvanced/jvm/ex2/Location; #43 = Utf8testStringCreate #44 = Utf8location #45 = Utf8str #46 = Utf8str1 #47 = Utf8testStringIntern #48 = Utf8a #49 = Utf8b #50 = Utf8StackMapTable #51 = Class#79// java/lang/String #52 = Utf8testStringConcat #53 = Utf8hello #54 = Utf8hel #55 = Utf8lo #56 = Utf8hello1 #57 = Utf8hello2 #58 = Utf8hello3 #59 = Class#101// java/io/PrintStream #60 = Utf8main #61 = Utf8([Ljava/lang/String; )V #62 = Utf8args #63 = Utf8[Ljava/lang/String; #64 = Utf8getCity #65 = Utf8()Ljava/lang/String; #66 = Utf8setCity #67 = Utf8(Ljava/lang/String; )V #68 = Utf8getRegion #69 = Utf8setRegion #70 = Utf8SourceFile #71 = Utf8Location.java #72 = NameAndType#36:#37// "":()V #73 = Utf8com/example/javaadvanced/jvm/ex2/Location #74 = Utf8深圳 #75 = NameAndType#66:#67// setCity:(Ljava/lang/String; )V #76 = Utf8南山 #77 = NameAndType#69:#67// setRegion:(Ljava/lang/String; )V #78 = Utf8abc #79 = Utf8java/lang/String #80 = Utf8abcd #81 = NameAndType#36:#67// "":(Ljava/lang/String; )V #82 = Utf8king #83 = NameAndType#102:#65// intern:()Ljava/lang/String; #84 = Class#103// java/lang/System #85 = NameAndType#104:#105// out:Ljava/io/PrintStream; #86 = Utf8a==b #87 = Class#101// java/io/PrintStream #88 = NameAndType#106:#67// println:(Ljava/lang/String; )V #89 = Utf8a!=b #90 = NameAndType#106:#107// println:(Z)V #91 = Utf8java/lang/StringBuilder #92 = NameAndType#108:#109// append:(Ljava/lang/String; )Ljava/lang/StringBuilder; #93 = NameAndType#110:#65// toString:()Ljava/lang/String; #94 = Utf8appleorange #95 = NameAndType#43:#37// testStringCreate:()V #96 = NameAndType#52:#37// testStringConcat:()V #97 = NameAndType#47:#37// testStringIntern:()V #98 = NameAndType#33:#34// city:Ljava/lang/String; #99 = NameAndType#35:#34// region:Ljava/lang/String; #100 = Utf8java/lang/Object #101 = Utf8java/io/PrintStream #102 = Utf8intern #103 = Utf8java/lang/System #104 = Utf8out #105 = Utf8Ljava/io/PrintStream; #106 = Utf8println #107 = Utf8(Z)V #108 = Utf8append #109 = Utf8(Ljava/lang/String; )Ljava/lang/StringBuilder; #110 = Utf8toString { public com.example.javaadvanced.jvm.ex2.Location(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1// Method java/lang/Object."":()V 4: return LineNumberTable: line 3: 0 LocalVariableTable: StartLengthSlotNameSignature 050thisLcom/example/javaadvanced/jvm/ex2/Location; public static void testStringCreate(); descriptor: ()V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=3, args_size=0 0: new#2// class com/example/javaadvanced/jvm/ex2/Location 3: dup 4: invokespecial #3// Method "":()V 7: astore_0 8: aload_0 9: ldc#4// String 深圳 11: invokevirtual #5// Method setCity:(Ljava/lang/String; )V 14: aload_0 15: ldc#6// String 南山 17: invokevirtual #7// Method setRegion:(Ljava/lang/String; )V 20: ldc#8// String abc 22: astore_1 23: new#9// class java/lang/String 26: dup 27: ldc#10// String abcd 29: invokespecial #11// Method java/lang/String."":(Ljava/lang/String; )V 32: astore_2 33: return LineNumberTable: line 11: 0 line 12: 8 line 13: 14 line 17: 20 line 23: 23 line 26: 33 LocalVariableTable: StartLengthSlotNameSignature 8260 locationLcom/example/javaadvanced/jvm/ex2/Location; 23111strLjava/lang/String; 3312str1Ljava/lang/String; public static void testStringIntern(); descriptor: ()V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=2, args_size=0 0: new#9// class java/lang/String 3: dup 4: ldc#12// String king 6: invokespecial #11// Method java/lang/String."":(Ljava/lang/String; )V 9: invokevirtual #13// Method java/lang/String.intern:()Ljava/lang/String; 12: astore_0 13: new#9// class java/lang/String 16: dup 17: ldc#12// String king 19: invokespecial #11// Method java/lang/String."":(Ljava/lang/String; )V 22: invokevirtual #13// Method java/lang/String.intern:()Ljava/lang/String; 25: astore_1 26: aload_0 27: aload_1 28: if_acmpne42 31: getstatic#14// Field java/lang/System.out:Ljava/io/PrintStream; 34: ldc#15// String a==b 36: invokevirtual #16// Method java/io/PrintStream.println:(Ljava/lang/String; )V 39: goto50 42: getstatic#14// Field java/lang/System.out:Ljava/io/PrintStream; 45: ldc#17// String a!=b 47: invokevirtual #16// Method java/io/PrintStream.println:(Ljava/lang/String; )V 50: return LineNumberTable: line 36: 0 line 41: 13 line 43: 26 line 44: 31 line 46: 42 line 49: 50 LocalVariableTable: StartLengthSlotNameSignature 13380aLjava/lang/String; 26251bLjava/lang/String; StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 42 locals = [ class java/lang/String, class java/lang/String ] frame_type = 7 /* same */public static void testStringConcat(); descriptor: ()V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=7, args_size=0 0: ldc#18// String hello 2: astore_0 3: ldc#19// String hel 5: astore_1 6: ldc#20// String lo 8: astore_2 9: ldc#18// String hello 11: astore_3 12: getstatic#14// Field java/lang/System.out:Ljava/io/PrintStream; 15: aload_0 16: aload_3 17: if_acmpne24 20: iconst_1 21: goto25 24: iconst_0 25: invokevirtual #21// Method java/io/PrintStream.println:(Z)V 28: new#22// class java/lang/StringBuilder 31: dup 32: invokespecial #23// Method java/lang/StringBuilder."":()V 35: ldc#19// String hel 37: invokevirtual #24// Method java/lang/StringBuilder.append:(Ljava/lang/String; )Ljava/lang/StringBuilder; 40: aload_2 41: invokevirtual #24// Method java/lang/StringBuilder.append:(Ljava/lang/String; )Ljava/lang/StringBuilder; 44: invokevirtual #25// Method java/lang/StringBuilder.toString:()Ljava/lang/String; 47: astore4 49: getstatic#14// Field java/lang/System.out:Ljava/io/PrintStream; 52: aload_0 53: aload4 55: if_acmpne62 58: iconst_1 59: goto63 62: iconst_0 63: invokevirtual #21// Method java/io/PrintStream.println:(Z)V 66: new#22// class java/lang/StringBuilder 69: dup 70: invokespecial #23// Method java/lang/StringBuilder."":()V 73: aload_1 74: invokevirtual #24// Method java/lang/StringBuilder.append:(Ljava/lang/String; )Ljava/lang/StringBuilder; 77: aload_2 78: invokevirtual #24// Method java/lang/StringBuilder.append:(Ljava/lang/String; )Ljava/lang/StringBuilder; 81: invokevirtual #25// Method java/lang/StringBuilder.toString:()Ljava/lang/String; 84: astore5 86: getstatic#14// Field java/lang/System.out:Ljava/io/PrintStream; 89: aload_0 90: aload5 92: if_acmpne99 95: iconst_1 96: goto100 99: iconst_0 100: invokevirtual #21// Method java/io/PrintStream.println:(Z)V 103: getstatic#14// Field java/lang/System.out:Ljava/io/PrintStream; 106: aload4 108: aload5 110: if_acmpne117 113: iconst_1 114: goto118 117: iconst_0 118: invokevirtual #21// Method java/io/PrintStream.println:(Z)V 121: ldc#26// String appleorange 123: astore6 125: return LineNumberTable: line 58: 0 line 59: 3 line 60: 6 line 64: 9 line 65: 12 line 68: 28 line 69: 49 line 71: 66 line 72: 86 line 73: 103 line 76: 121 line 77: 125 LocalVariableTable: StartLengthSlotNameSignature 31230 helloLjava/lang/String; 61201helLjava/lang/String; 91172loLjava/lang/String; 121143 hello1Ljava/lang/String; 49774 hello2Ljava/lang/String; 86405 hello3Ljava/lang/String; 12516strLjava/lang/String; StackMapTable: number_of_entries = 8 frame_type = 255 /* full_frame */ offset_delta = 24 locals = [ class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ] stack = [ class java/io/PrintStream ] frame_type = 255 /* full_frame */ offset_delta = 0 locals = [ class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ] stack = [ class java/io/PrintStream, int ] frame_type = 255 /* full_frame */ offset_delta = 36 locals = [ class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ] stack = [ class java/io/PrintStream ] frame_type = 255 /* full_frame */ offset_delta = 0 locals = [ class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ] stack = [ class java/io/PrintStream, int ] frame_type = 255 /* full_frame */ offset_delta = 35 locals = [ class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ] stack = [ class java/io/PrintStream ] frame_type = 255 /* full_frame */ offset_delta = 0 locals = [ class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ] stack = [ class java/io/PrintStream, int ] frame_type = 80 /* same_locals_1_stack_item */ stack = [ class java/io/PrintStream ] frame_type = 255 /* full_frame */ offset_delta = 0 locals = [ class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ] stack = [ class java/io/PrintStream, int ]public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String; )V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=0, locals=1, args_size=1 0: invokestatic#27// Method testStringCreate:()V 3: invokestatic#28// Method testStringConcat:()V 6: invokestatic#29// Method testStringIntern:()V 9: return LineNumberTable: line 82: 0 line 83: 3 line 84: 6 line 86: 9 LocalVariableTable: StartLengthSlotNameSignature 0100args[Ljava/lang/String; public java.lang.String getCity(); descriptor: ()Ljava/lang/String; flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield#30// Field city:Ljava/lang/String; 4: areturn LineNumberTable: line 89: 0 LocalVariableTable: StartLengthSlotNameSignature 050thisLcom/example/javaadvanced/jvm/ex2/Location; public void setCity(java.lang.String); descriptor: (Ljava/lang/String; )V flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: putfield#30// Field city:Ljava/lang/String; 5: return LineNumberTable: line 93: 0 line 94: 5 LocalVariableTable: StartLengthSlotNameSignature 060thisLcom/example/javaadvanced/jvm/ex2/Location; 061cityLjava/lang/String; public java.lang.String getRegion(); descriptor: ()Ljava/lang/String; flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield#31// Field region:Ljava/lang/String; 4: areturn LineNumberTable: line 97: 0 LocalVariableTable: StartLengthSlotNameSignature 050thisLcom/example/javaadvanced/jvm/ex2/Location; public void setRegion(java.lang.String); descriptor: (Ljava/lang/String; )V flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: putfield#31// Field region:Ljava/lang/String; 5: return LineNumberTable: line 101: 0 line 102: 5 LocalVariableTable: StartLengthSlotNameSignature 060thisLcom/example/javaadvanced/jvm/ex2/Location; 061 regionLjava/lang/String; } SourceFile: "Location.java"

常见面试题剖析 新生代为什么要分成eden,s0,s1三个区域,为什么不能分成两个区域,三个区域的比例是多少,为什么是这个比例 【Android面试题|Java进阶之深入理解对象与垃圾回收机制常见面试题】因为新生代使用的是复制算法,大部分对象都是朝生夕死的,如果只有两个区域,则会有50%的内存空间是浪费的,分为三个区域可以提高空间利用率,三个区域的比例是8:1:1,空间利用率由50%提高到90%
说一下JVM内存结构 开放式题目,具体可见章节 运行时数据区域
一般从两个维度出发:线程私有和线程共享。到每一个内存区域的细节点。
Android面试题|Java进阶之深入理解对象与垃圾回收机制常见面试题
文章图片

Java 虚拟机栈是基于线程的。哪怕你只有一个 main() 方法,也是以线程的方式运行的。在线程的生命周期中,参与计算的数据会频繁地入栈和出栈,栈的生命周期是和线程一样的。
栈里的每条数据,就是栈帧。在每个 Java 方法被调用的时候,都会创建一个栈帧,并入栈。一旦完成相应的调用,则出栈。所有的栈帧都出栈后,线程也就结束了。每个栈帧,都包含四个区域:局部变量表、操作数栈、动态连接、返回地址
本地方法栈是和虚拟机栈非常相似的一个区域,它服务的对象是 native 方法。
程序计数器是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。
堆是 JVM 上最大的内存区域,我们申请的几乎所有的对象,都是在这里存储的。我们常说的垃圾回收,操作的对象就是堆。
方法区,这个区域存储的内容,包括:类的信息、常量池、方法数据、方法代码。
什么情况下内存栈溢出? java.lang.StackOverflowError : 如果出现了可能会是无限递归。
java.lang.OutOfMemoryError:不断建立线程,JVM不断申请栈内存,当机器没有足够的栈内存时。
描述new一个对象的流程 Android面试题|Java进阶之深入理解对象与垃圾回收机制常见面试题
文章图片

Java对象会不会分配在栈中? 可以,如果这个对象不满足逃逸分析,即如果对象发生了逃逸,那么虚拟机在特定的情况下会走栈上分配。
如何判断一个对象是否应该被回收,有哪些算法,实际虚拟机使用得最多的是什么? 引用计数法和根可达性分析两种,用得最多是根可达性分析。
GC收集算法有哪些?他们的特点是什么? 复制算法、标记清除算法、标记整理算法。
复制算法速度快,不会产生内存碎片,但是要浪费内存空间。
标记清除算法空间利用率高,不需要移动对象,但是有内存碎片产生。
标记整理算法没有产生内存碎片,但是要移动对象,性能较低。
三种算法各有所长,各有所短。
JVM中一次完整的GC流程是怎样的?对象如何晋级到老年代? 对象优先在新生代区中分配,若没有足够空间,Minor GC;
大对象(需要大量连续内存空间)直接进入老年态;长期存活的对象进入老年态。
如果对象在新生代出生并经过第一次MGC后仍然存活,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。
Java中的几种引用关系,他们的区别是什么? 强引用
一般的Object obj = new Object() ,就属于强引用。在任何情况下,只有有强引用关联(与根可达)还在,垃圾回收器就永远不会回收掉被引用的对象。
软引用 SoftReference
一些有用但是并非必需,用软引用关联的对象,系统将要发生内存溢出(OuyOfMemory)之前,这些对象就会被回收(如果这次回收后还是没有足够的空间,才会抛出内存溢出)。
弱引用 WeakReference
一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。
虚引用 PhantomReference
幽灵引用,最弱(随时会被回收掉)
对象被垃圾回收的时候收到一个通知。
final、finally、finalize的区别? final
在java中,final可以用来修饰类,方法和变量(成员变量或局部变量)
当用final修饰类的时,表明该类不能被其他类所继承。当我们需要让一个类永远不被继承,此时就可以用final修饰,但要注意:
final类中所有的成员方法都会隐式的定义为final方法。
使用final方法的原因主要有两个:
(1) 把方法锁定,以防止继承类对其进行更改。
(2) 效率,在早期的java版本中,会将final方法转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要final方法进行这些优化了。
final成员变量表示常量,只能被赋值一次,赋值后其值不再改变。
finally
finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下
finalize
Object中的Finalize()方法:即使通过可达性分析判断不可达的对象,也不是“非死不可”,它还会处于“缓刑”阶段,真正要宣告一个对象死亡,需要经过两次标记过程,一次是没有找到与GCRoots的引用链,它将被第一次标记。随后进行一次筛选(如果对象覆盖了finalize),我们可以在finalize中去拯救。
所以建议大家尽量不要使用finalize,因为这个方法太不可靠。在生产中你很难控制方法的执行或者对象的调用顺序,建议大家忘了finalize方法!因为在finalize方法能做的工作,java中有更好的,比如try-finally或者其他方式可以做得更好。
String s = new String(“xxx”); 创建了几个对象? 2个:
1、在一开始字符串"xxx"会在加载类时,在常量池中创建一个字符串对象。
2、调用 new时 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中的字符串对象。

    推荐阅读