其他分享
首页 > 其他分享> > 6 方法区

6 方法区

作者:互联网

6.1 定义

是所有java虚拟机线程共享的,他存储了跟类的结构相关的信息(成员方法,构造器。。。)

方法区在虚拟机启动时被创建,逻辑上是堆的一个组成部分。但是厂商在设置的时候有不同,有些把方法区放堆中,有些没有。

方法区如果内存不足了,也会抛一个内存不足错误。

在1.6里面方法区的实现叫做永久代。1.8里面实现叫元空间

 

 

 

 注意1.8里面,他把StringTable不再放到方法区里面,而是放到了堆空间。

6.2 内存溢出

1.8之前会导致永久代内存溢出

 

 

 

 1.8之后会导致元空间内存溢出

 

 元空间使用的是系统内存,并不会导致内存溢出。所以我们要加一个参数MaxMetaspaceSize限制元空间大小。

场景:在运行期间动态生成类的字节码完成动态类加载

在实际中我们框架里面大量使用这些加载类,1.8之前还是很容易导致永久代内存溢出,当然1.8以后,元空间使用的是系统内存,空间相对大了。并且垃圾回收效率高。

6.3 常量池

不管是1.6还是1.8,他们都有一个运行时常量池的部分。那这个运行时常量池的内部包含一个叫StringTable的东西,那他到底是什么呢?我们想要了解运行时常量池,首先要来了解一下什么叫常量池。

 

 

上面这段代码想要运行,首先要编译为字节码,那字节码宏观来说由三部分组成(类的基本信息,常量池,类方法定义-包含虚拟机指令)

 

 为了搞清楚上面的答案,我们需要从字节码和常量池的角度来分析一下刚才这些代码他的底层原理。

 

 

那么常量池和串池之间有有什么关系呢?

 

 tostring()的底层实现new出一个对象

 

 综上得出结果

 

 String s5 = "a" + "b";

s3和s5相等,

 

 

 

 

地址都是一样的,并且s5找的时候直接去常量池的#4的位置找ab这个串。那这个他底层是怎么做的呢?

6.7.2 字符串延迟加载

 

 

上面这些代码,并不是说一开始就全部把字符串加进串池里面去,而是我执行一行,就加一个进去。

如果再执行下面的代码。串池数量会变吗?

 

 

不会变,还是2285,串池中字符串对象是唯一的,不会变

第二个实例验证

 

 

6.7.3总结stringtable的特性

 

 

6.7.4 1.8中的intern()

那我能不能主动的把我的ab存入串池呢?在jdk1.8中可以调用s.intern()方法,将这个字符串对象尝试放入串池,如果没有就放入,有的话就不放。不管有没有,会把串池中的对象返回。

 

 

 

 

疑问:// s 不是堆区的吗,怎么会和常量池的 "ab" 相等?

现在我们修改一下代码:

 public class test01 {
        public static void main(String[] args) {
            String ab = "ab";
            String s = new String("a") + new String("b");  // 堆区["ab"]
            String s2 = s.intern();  // 串池中已经有了"ab",那就不会把堆区的s字符串对象剪切进去,返回的是串池的"ab"
            System.out.println(s2 == "ab"); // true
            System.out.println(s == "ab");  // false  s是堆区的  "ab"是串池的 当然为false     
        }

6.7.5 1.6中的intern()

1,6中的规则略有不同,当你的串池中没有你要放入的这个对象时,他会复制一份放入串池。相当于ctrl + c 和ctrl+v

 

6.7.4 面试题再解析

// 串池[s1:"a", s2:"b", s3 s5 s6:"ab" x1:"cd"]
    public class test01 {
        public static void main(String[] args) {
            String s1 = "a";
            String s2 = "b";
            String s3 = "a" + "b";   // 编译期优化
            String s4 = s1 + s2;    // 堆区[ s4:"ab" x2:"cd"
            String s5 = "ab"; // 因为常量池中已经有"ab",直接引用有的
            String s6 = s4.intern();
            System.out.println(s3 == s4);  
            System.out.println(s3 == s5);  
            System.out.println(s3 == s6); 
            System.out.println("==============");
            String x1 = "cd";
            String x2 = new String("c") + new String("d");
            x2.intern();
            System.out.println(x1 == x2);  // false

        }

如果是1.6里面:

    String x2 = new String("c") + new String("d");
    x2.intern();
    String x1 = "cd";
    System.out.println(x1 == x2); 

 

这里呢x2.intern并不是x2入池,而是x2的副本入池,所以x1和x2是不一样的

6.7.5 StringTable的位置

 

 

1.6->1.8为什么要做这个更改呢?

6.7.6 StringTable 垃圾回收‘

加上这些参数能看到具体信息

 

 内存不够的时候,就触发一次垃圾回收

 

 加了1万个字符串进去,但是实际只容纳了7226个,因为内存紧张触发了GC的垃圾回收机制。

 

 

6.7.7 StringTable的调优

StringTable的底层是一个哈希表,哈希表如果桶的个数多,元素相对分散,哈希碰撞的几率小,查找速度快。反之则慢。那么调优的话主要就是调整桶的个数

 

 

如果string的数量比较大的话,那就把桶的数量调的大一点。减少哈希冲突。

twitter需要大量存储用户的地址信息,需要用30G内存才能存下,但是用户地址很多都是重复的,如果我们不加区分把重复地址都存进去,那占用空间大。他们采用方法就是用intern方法,相同地址在串池中只会占用一份,使用了之后啊。下降到了几百M。

 

标签:ab,String,StringTable,串池,字符串,方法,常量
来源: https://www.cnblogs.com/YXBLOGXYY/p/15396425.html