一、设计思想及原理

设计思想

1、字符串分配和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能。

2、JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。

为字符串开辟一个字符串常量池,类似于缓存区。

创建字符串常量时,首先查询字符串常量池是否存在该字符串。

存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中。

设计原理

底层是hotspot的C++实现,类似一个 HashTable, 保存的本质上是字符串对象的引用。

二、三种字符串创建

Jdk1.7 及以上版本

1.直接赋值

String s = "tom";     // 只会在字符串常量池中,s指向常量池中的引用

创建对象时,JVM会先去常量池中通过 equals(key) 方法,判断是否有相同的对象, 有则直接返回该对象在常量池中的引用,没有则会在常量池中创建一个新对象,再返回引用。

2.new String()

 String s = new String("tom");           // 字符串常量池和堆中都有这个对象,s指向内存中的对象引用

创建对象时,先检查字符串常量池中是否有字符串,有则直接去堆内存中创建一个字符串对象,没有则先在字符串常量池里创建一个字符串对象,再去内存中创建一个字符串对象,最后将内存中的引用返回。

3.String.intern()方法

String s1 = new String("tom");
String s2 = s1.intern();
System.out.println(s1 == s2); //false

是一个 native 的方法,当调用 intern方法时,如果字符串常量池已经包含一个等于此字符串对象的字符串(用equals(oject)方法确定),则返回常量池中的字符串。否则,将intern返回的引用指向当前字符串 s1(jdk1.6版本需要将 s1 复制到字符串常量池里)。

三、字符串常量池位置

Jdk1.6及之前: 有永久代,运行时常量池在永久代,运行时常量池包含字符串常量池。

Jdk1.7:有永久代,但已经逐步“去永久代”,字符串常量池从永久代里的运行时常量池分离到堆里。

Jdk1.8及之后: 无永久代,运行时常量池在元空间,字符串常量池里依然在堆里。 字符串常量池在堆里

验证 Demo

public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
for (int i = 0; i < 10000000; i++) {
String str = String.valueOf(i).intern();
list.add(str);
}
}
}

配置JVM参数

jdk6:‐Xms6M ‐Xmx6M ‐XX:PermSize=6M ‐XX:MaxPermSize=6M

jdk8:‐Xms6M ‐Xmx6M ‐XX:MetaspaceSize=6M ‐XX:MaxMetaspaceSize=6M

运行结果

jdk7及以上:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

jdk6:Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

——————仅供学习使用

String在内存中如何分布的更多相关文章

  1. 一个 -100.01 的double 在内存中怎么存储的. 一个中文String 在内存中占多少直接 utf-8 / GBK

    一.-100.01 的double 在内存中怎么存储的 double双精度数据类型存储格式IEEE 双精度格式为8字节64位,由三个字段组成:52 位小数 f : 11 位偏置指数 e :以及 1 位 ...

  2. C#中string在内存中是如何表示的

    不知道你是否有过和我一样的疑问,不同编码的字符串是如何存储在运行时的内存中的呢,计算机在操作string类型的对象时,如何知道这个string是什么编码呢?和文本文件那样有类似BOM的东东在strin ...

  3. String在内存中如何存储(Java)

    JDK1.8中JVM把String常量池移入了堆中,同时取消了“永久代”,改用元空间代替(Metaspace)java中对String对象特殊对待,所以在heap区域分成了两块,一块是字符串常量池(S ...

  4. String 在内存中如何存储的

    基本数据类型由于长度固定,且需要空间比较少,所以直接存储在栈中:而对象比较大,所以栈中只存储一个4btye的引用地址(逻辑地址). java中对String对象特殊对待,所以在heap区域分成了两块: ...

  5. C++程序中不同变量、函数在内存中内存中的分布情况

    一.一个C++编译的程序占用的内存分为以下几个部分 1.栈区:由编译器自动分配 存放函数的参数值,局部变量的值等,操作方式类似于数据结构中的栈. 2.堆区:一般由程序员分配释放,若程序员不释放,程序结 ...

  6. c++ 程序在内存中的分布

    从低地址到高地址: 1.代码区[包含常量的]:存放函数体的二进制代码 2.全局变量区[已初始化 + 未初始化]: 全局变量和静态变量的存储是放一块的,初始化的全局变量和静态变量在一块区域, 未初始化的 ...

  7. 二维数组int[3][2]在内存中的分布方式

  8. 【问】Windows下C++局部变量在内存中的分布问题

    原本是为了看看C++对象模型中子对象赋值给一个父对象和父类型指针指向的域时,到底会不会切割,就打开codebloks写了下面的代码,编译器选的是GNU. #define DEBUG(X) std::c ...

  9. Java数组在内存中是如何存放的

    阅读目录 一维数组 二维数组 数组对象及其引用存放在内存中的哪里? Java中有两种类型的数组: 基本数据类型数组: 对象数组: 当一个对象使用关键字“new”创建时,会在堆上分配内存空间,然后返回对 ...

  10. Delphi接口的底层实现(接口在内存中仍然有其布局,它依附在对象的内存空间中,有汇编解释)——接口的内存结构图,简单清楚,深刻 good

    引言 接口是面向对象程序语言中一个很重要的元素,它被描述为一组服务的集合,对于客户端来说,我们关心的只是提供的服务,而不必关心服务是如何实现的:对于服务端的类来说,如果它想实现某种服务,实现与该服务相 ...

随机推荐

  1. go goroutine 怎样更好的进行错误处理

    前言 在 Go 语言程序开发中,goroutine 的使用是比较频繁的,因此在日常编码的时候 goroutine 里的错误处理,怎么做会比较好呢? 一般我们的业务代码如下: func main() { ...

  2. 哈希表(实现 Python 中的集合 set)

    博客地址:https://www.cnblogs.com/zylyehuo/ # -*- coding: utf-8 -*- class LinkList: class Node: def __ini ...

  3. 多主机网络下 Docker Swarm 模式的容器管理

    分类专栏: Services/Server Management Linux Basics Linux资讯 http://www.linuxprobe.com/thread版权导读    本文将以多主 ...

  4. Ubuntu安装GPU驱动+CUDA+cuDNN的安装方法

    一台有GPU的虚拟机如果没有安装CUDA的驱动,是需要我们手动去进行安装的,介绍Ubuntu操作系统的安装教程. 1. 下载安装文件 NVIDIA CUDA Toolkit Archive 点击上面链 ...

  5. 智能Agent如何改造传统工作流:从搜索到全能助手

    智能Agent如何改造传统工作流:从搜索到全能助手 引言:当AI遇上工作流 还记得我们以前搜索信息的方式吗?输入关键词,浏览大量结果,筛选有用内容,再整合成我们需要的答案.这个过程不仅耗时,还常常让人 ...

  6. [每日算法] leetcode第3题:无重复字符的最长子串

    leetcode第3题入口 题目描述 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: s = "abcabcbb" 输出: 3 解法1: ...

  7. 代码块--java进阶day03

    1.代码块 1.局部代码块 定义在方法中的一对大括号,可以提早释放内存,走完{}里的逻辑后就会被释放,在之后的编程中无法使用 2.构造代码块 位置在类中,方法外的{},在构造方法执行的时候,构造代码块 ...

  8. 多态的引入--java进阶day02

    1.多态的介绍 总的来说就是一句话,使用多态,所有的子类都可以根据父类这个桥梁来连接它们各自的成员方法,从而调用方法,减少多次的代码重写,使代码更加简单便捷 我们以之前说的公司写业务为例子来理解多态, ...

  9. LinkedBlockingQueue的take方法底层源码

    一.LinkedBlockingQueue的take方法底层源码 LinkedBlockingQueue 的 take 方法是其核心方法之一,用于从队列头部移除并返回元素.如果队列为空,调用 take ...

  10. 🎀idea-java序列化serialversionUID自动生成

    简介 java.io.Serializable 是 Java 中的一个标记接口(marker interface),它没有任何方法或字段.当一个类实现了 Serializable 接口,那么这个类的对 ...