Java内存区域与内存溢出
  Java虚拟机中的内存分配图:

  各个区域的特性总结如下表:

  补充说明:
  当多线程情形下,可能多个线程要在堆上分配内存,那么可能出现内存分配的同步问题,解决方案有两个,一个是同步内存分配动作;另一个是采用TLAB,即在Java堆中针对每个线程先预先分配一小块线程私有的本地线程分配缓冲。这样当线程需要分配内存时在自己的TLAB上进行,从而避免同步的开销。但是当TLAB分配满重新分配TLAB时仍需要同步;
  判断一个类是否属于无用的类,“可以”被回收的条件为:1 该类所有的实例都已经被回收;2 加载该类的ClassLoader已经被回收; 3 该类对于的java.lang.Class对象没有在任何地方被引用。注意,只是可以,而不是要被回收。
  内存分配方式:虚拟机使用哪种方式由内存是否规整决定,而内存是否规整则由回收算法决定。
  1、指针碰撞:假设所有已经被分配的内存放在一边,而空闲的放在另外一边,二者中间则用一个指针来作为分界点,当需要分配内存时只需要移动该指针即可,这样的方式称为指针碰撞。使用此方法的有Serial、ParNew等带Compact的回收器
  2、空闲列表:虚拟机中已分配的内存和空闲的内存并不规整,处于相互交错的情形,那么需要通过维护一个列表来表示哪些内存是可用,当需要分配内存时则需要通过查询列表来查找可用大小的内存。这样的方式称为空闲列表。使用此方法的有CMS等这种基于Mark-Sweep算法的回收器
  对象在HotSpot虚拟机中的内存布局如下表所示:

  在Java规范中,对于reference类型只规定了一个指向对象的引用,但没有规定通过何种方式去访问引用的数据。因此对于不同的虚拟机也有不同的访问方式,主要有两种方式:
  1、使用句柄:在Java堆中划出一块区域作为句柄池来存储句柄,一个句柄包括对象实例数据的指针以及对象数据类型的指针,reference中存储的是对象的句柄的地址。reference通过句柄来间接访问对象。其好处在于:当对象移动时,只需要改动矩形中的指针即可,而相应的reference则不需要做变动;
  2、直接访问:reference存储的是对象地址,通过reference可以直接访问数据。Java对中的数据对象中含有到对象数据类型的指针,比如上面提到的类型指针。此种方式的好处是访问速度快,相比使用句柄的方式而言少了一次指针定位的开销。HotSpotVM使用的是此种方式。
  两种使用方式的图示说明如下图: