String内存模型

1 、Java内存模型: 
编程时你需要考虑的不是内存的物理地址(memory  address),而是一种逻辑上的内存模型。Java虚拟机将其管辖的内存大致分三个逻辑部分:方法区(Method  Area)、Java栈和Java堆。 

方法区是静态分配(static  allocation)的,编译器将变量在绑定在某个存储位置上,而且这些绑定不会在运行时改变。Java方法区的一个 重要部分,也是静态分配最典型的例子,是常数池,源代码中的命名常量、String常量和static  变量保存在其中。 
Java  Stack是一个逻辑概念,特点是后进先出,此外没有特别的要求。Java  Stack并不代表任何特定的内存区间,也不限制它的实现方式。一 个栈的空间可能是连续的,也可能是不连续的。最典型的Stack应用是方法的调用,Java虚拟机每调用一次方法就创建一个方法帧(frame),退出该 方法则对应的方法帧被弹出(pop)。栈分配存在一些局限:Java栈所处理的方法帧和局部变量,都将随着方法帧弹出而结束,显然局部变量无法从一个帧保 持到下一个帧,被调方法的 帧不可能比调用方法的寿命更长。因此,从数据保存来看,栈分配适用于由作用域决定生命周期的局部变量。 
Java堆(Heap)堆分配(heap  allocation)意味着以随意的顺序,在运行时进行存储空间分配和收回的内存管理模型。堆中存储的数据常常是大小、数量和生命期在编译时无法确定的。Java对象的内存总是在heap中分配。 

2、有人问,基本类型变量和引用变量在哪里分配空间,这不是一个有效的问题。基本类型变量和引用变量都可能保存在stack或heap中,关键是看它们声明的位置——到底是域还是局部变量。参考,“引用在哪里分配空间?” 
http://blog.csdn.net/yqj2065/archive/2008/11/25/3365726.aspx 

3、“封装器”(wrapper)类。事实上,Integer、Double等包装类和String有着同样的特性:不变类。 
String  str  "abc"的内部工作机制很有代表性,这里我以Boolean为例,说明同样的问题。 
不变类的属性一般定义为final,一旦构造完毕就不能再改变了。 
于是private  final  boolean  value; 
而且由于Boolean对象只有有限的两种状态:true和false,将这两个Boolean对象定义为命名常量: 
public  static  final  Boolean  TRUE  new  Boolean(true); 
public  static  final  Boolean  FALSE  new  Boolean(false); 
这两个命名常量和字符串常量一样,在常数池中分配空间。 
你注意到,Boolean.TRUE是一个引用,Boolean.FALSE是一个引用,而"abc"也是一个引用!(虽然我们经常把它混用地说成字符串对象。) 

由于Boolean.TRUE是类变量(static)将静态地分配内存,所以需要很多Boolean对象时,并不需要用new表达式创建各个实例,完全可以共享这两个静态变量。其JDK中源代码是: 
public  static  Boolean  valueOf(boolean  b) 
return  (b  TRUE  FALSE); 

基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unboxing)是JSE  5.0提供的新功能。自动装箱与拆箱的功能事实上是编译器帮了你一点忙, 
Boolean  b1  5>3; 
等价于Boolean  b1  Boolean.valueOf(5>3); 
//优于Boolean  b1  new  Boolean  (5>3); 

        static  void  foo(){ 
                boolean  isTrue  5>3;    //基本类型 
                Boolean  b1  Boolean.TRUE;  //静态变量创建的对象 
                Boolean  b2  Boolean.valueOf(isTrue);//静态工厂 
                Boolean  b3  5>3;//自动装箱(autoboxing) 
                System.out.println("b1  ==  b2  ?"  +(b1  ==  b2)); 
                System.out.println("b1  ==  b3  ?"  +(b1  ==  b3)); 
               
                Boolean  b4  new  Boolean(isTrue);不宜使用 
                System.out.println("b1  ==  b4  ?"  +(b1  ==  b4));//浪费内存、有创建实例的时间开销 
       
这里b1、b2、b3指向同一个Boolean对象。 

4、String的特殊性 
由于String使用非常频繁,String常量也在常数池中静态分配内存。String  str  "abc"的内部工作是这样的: 
Java编译器首先扫描整个程序(简化地说),把其中所有的String常量收集起来,不同则分别“创建”一个String对象。 
String  str;其中str是一个引用。注意"abc"是一个引用。 
因此: 
String  str1  "abc"; 
String  str2  "abc"; 
String  str3  "ab"+"c";//同样 
//str1  "bcd"; 
System.out.println(str1==str2);  //true 
这里,str1、str2和  "abc"指向同一个String对象。它们都指向常数池中的那个对象。 
==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回true。 
如果str1指向同"bcd",则输出false。 

再举一例: 
public  class  A{ 
        String  ="abc"; 

public  class  B{ 
        String  ="abc"; 

public  class  User{ 
        static  void  foo(){ 
                new  A(); 
                new  B(); 
                System.out.println(a.x==b.x); 
       

这里,System.out.println(a.x==b.x);也输出true。 

String  str1  new  String("abc");  如何工作? 
和 
Boolean  b4  new  Boolean(isTrue);不宜使用 
一样,被我称为“傻瓜的代码”,由于new而在heap中创建一个String对象,并将其引用赋值给str1, 
因此System.out.println(str1=="abc");输出false。 

如果问你:String  ="abc";创建了几个对象? 
准确的答案是:0或者1个。如果存在"abc",则变量x持有"abc"这个引用,而不创建任何对象。 
如果问你:String  str1  new  String("abc");  创建了几个对象? 
准确的答案是:1或者2个。(至少1个在heap中) 

5、结论与建议: 
(1)对语句String  str  "abc";有人想当然地认为,创建了String类的对象str(注意,str常常被混用,称为“对象”,初学 者最好简明地称它为“变量”,而不说“String类型的引用变量”)。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,变量str持有了 String引用"abc"。罗罗嗦嗦的说法,我们声明了一个String类引用变量str,它被赋值了一个String的引用,这个引用是"abc"。 (清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。信不信随你) 


(2)对于String  str  new  String("abc");的代码,记住它是“傻瓜的代码”,既浪费内存、又有创建实例的时间开销。所谓“享元模式的思想”,你暂时别管它。 

(3)比较两个对象的状态是否相等时,用你override后的Object.equals()方法;当测试两个引用变量是否指向同一个对象时,用obj1==obj2。 

(4)对于immutable类如String,必要时设计一个配套的非不变类如StringBuffer;只要有可能,将类设计成不变类。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocal内存模型是指通过ThreadLocal类来实现的一种特殊的线程封闭机制,它可以让每个线程都拥有自己的变量副本,从而实现线程间的数据隔离。 在使用ThreadLocal时,每个线程都有自己独立的ThreadLocalMap实例,该实例存储了线程自身的ThreadLocal变量。实际上,数据是保存在当前的Thread对象上,而不是ThreadLocal对象上。ThreadLocal只是提供了一个操作的框架,用于将数据存储在当前线程的ThreadLocalMap中。 ThreadLocal内存泄漏是指当ThreadLocal对象被回收时,由于ThreadLocalMap中的Entry仍然持有对ThreadLocal对象的强引用,导致ThreadLocal对象无法被垃圾回收,从而造成内存泄漏。要避免内存泄漏,需要在不再使用ThreadLocal对象时手动调用其remove方法来清除对应的Entry。 ThreadLocal内存模型可以通过以下代码示例来观察: ```java import java.util.ArrayList; public class Main { static class ValueObject { private long[] data = new long[131072]; // 需要 1M 空间 (1024 * 1024 / 8) } public static void main(String[] args) throws InterruptedException { int threadNumber = 10; while (threadNumber-- > 0) { Thread worker = new Thread(() -> { int localCount = 15; var locals = new ArrayList<>(localCount); while (localCount-- > 0) { ThreadLocal<ValueObject> newLocal = new ThreadLocal<>(); newLocal.set(new ValueObject()); locals.add(newLocal); // newLocal.remove(); } locals = null; System.gc(); }, "工作线程"); worker.start(); worker.join(); } System.out.println("运行结束"); } } ``` 在上述代码中,每个工作线程创建了15个ThreadLocal实例,并将其添加到一个ArrayList中。在每个ThreadLocal实例中,我们设置了一个ValueObject对象作为值。如果没有手动调用remove方法来清除Entry,那么在垃圾回收时,这些Entry将持续引用ThreadLocal对象,导致内存泄漏。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [java(8)--线程ThreadLocal详解](https://blog.csdn.net/hguisu/article/details/8024799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [ThreadLocal 内存模型内存泄漏原因、现象观测、解决](https://blog.csdn.net/the_first_snow/article/details/105743395)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值