堆(Heap):Java堆(Java Heap)是Java虚拟机所管理的内存中大的一块。Java堆是被所有线程共享的一块内存区域。在此区域的目的是存放对象实例,几乎所有的对象实例都是在这里分配内存,但是这个对象的引用却是在栈(Stack)中分配。因此,执行String s = new String(“s”)时,需要从两个地方分配内存:在堆中为String对象分配内存,在栈中为引用(这个堆对象的内存地址,即指针)分配内存,如下图所示。

  JAVA虚拟机有一条在堆中分配新对象的指令,却没有释放内存的指令,正如你无法用Java代码区明确释放一个对象一样。虚拟机自己负责决定如何以及何时释放不再被运行的程序引用的对象所占据的内存,通常,虚拟机把这个任务交给垃圾收集器(Garbage Collection)。其相关设置参数:
  -Xms — 设置堆内存初始大小
  -Xmx — 设置堆内存大值
  -XX:MaxTenuringThreshold — 设置对象在新生代中存活的次数
  -XX:PretenureSizeThreshold — 设置超过指定大小的大对象直接分配在旧生代中
  Java堆是垃圾收集器管理的主要区域,因此又称为“GC 堆”(Garbage Collectioned Heap)。现在的垃圾收集器基本都是采用的分代收集算法,所以Java堆还可以细分为:新生代(Young Generation)和老年代(Old Generation),如下图所示。分代收集算法的思想:第一种说法,用较高的频率对年轻的对象(young generation)进行扫描和回收,这种叫做minor collection,而对老对象(old generation)的检查回收频率要低很多,称为major collection。这样不需要每次GC都将内存中所有对象都检查一遍,以便让出更多的系统资源供应用系统使用;另一种说法,在分配对象遇到内存不足时,先对新生代进行GC(Young GC);当新生代GC之后仍无法满足内存空间分配需求时, 才会对整个堆空间以及方法区进行GC(Full GC)。

  在这里可能会有读者表示疑问:记得还有一个什么代(Permanent Generation)的啊,难道它不属于Java堆?亲,你答对了!其实传说中的代是上面所说的方法区,存放的都是jvm初始化时加载器加载的一些类型信息(包括类信息、常量、静态变量等),这些信息的生存周期比较长,GC不会在主程序运行期对PermGen Space进行清理,所以如果你的应用中有很多CLASS的话,很可能出现PermGen Space错误。其相关设置参数:
  -XX:PermSize –设置Perm区的初始大小
  -XX:MaxPermSize –设置Perm区的大值
  新生代(Young Generation)又分为:Eden区和Survivor区,Survivor区有分为From Space和To Space。Eden区是对象初分配到的地方;默认情况下,From Space和To Space的区域大小相等。JVM进行Minor GC时,将Eden中还存活的对象拷贝到Survivor区中,还会将Survivor区中还存活的对象拷贝到Tenured区中。在这种GC模式下,JVM为了提升GC效率, 将Survivor区分为From Space和To Space,这样可以将对象回收和对象晋升分离开来。新生代的大小设置有2个相关参数:
  -Xmn — 设置新生代内存大小。
  -XX:SurvivorRatio — 设置Eden与Survivor空间的大小比例
  老年代(Old Generation): 当 OLD 区空间不够时, JVM 会在 OLD 区进行 major collection;完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”Out of memory错误”  。
  三、String类型的深度解析
  让我们从Java数据类型开始说起吧!Java数据类型通常(分类方法多种多样)从整体上可以分为两大类:基础类型和引用类型,基础类型的变量持有原始值,引用类型的变量通常表示的是对实际对象的引用,其值通常为对象的内存地址。对于基础类型和引用类型的细分,直接上图吧,大家看了一目了然。当然,下图也仅仅只是其中的一种分类方式。
  (原文图丢失)
  针对上面的图,有3点需要说明:
  char类型可以单独出来形成一类,很多基本类型的分类为:数值类型、字符型(char)和bool型。
  returnAddress类型是一个Java虚拟机在内部使用的类型,被用来实现Java程序中的finally语句。
  String类型在上图的什么位置?yes,属于引用类型下面的类类型。下面开始对String类型的挖掘!
  1、String的本质
  打开String的源码,类注释中有这么一段话“Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings.Because String objects are immutable they can be shared.”。这句话总结归纳了String的一个重要的特点:String是值不可变(immutable)的常量,是线程安全的(can be shared)。
  接下来,String类使用了final修饰符,表明了String类的第二个特点:String类是不可继承的。
  下面是String类的成员变量定义,从类的实现上阐明了String值是不可变的(immutable)。
  private final char value[];
  private final int count;
  因此,我们看String类的concat方法。实现该方法第一步要做的肯定是扩大成员变量value的容量,扩容的方法重新定义一个大容量的字符数组buf。第二步是把原来value中的字符copy到buf中来,再把需要concat的字符串值也copy到buf中来,这样子,buf中包含了concat之后的字符串值。下面是问题的关键了,如果value不是final的,直接让value指向buf,然后返回this,则大功告成,没有必要返回一个新的String对象。但是。。。可惜。。。由于value是final型的,所以无法指向新定义的大容量数组buf,那怎么办呢?“return new String(0, count + otherLen, buf);”,这是String类concat实现方法的后一条语句,重新new一个String对象返回。这下真相大白了吧!
  总结:String实质是字符数组,两个特点:1、该类不可被继承;2、不可变性(immutable)。