import java.util.LinkedHashMap;
import java.util.Collection;
import java.util.Map;
import java.util.ArrayList;
/**
* The generic implementation of a LRU (last recently used) cache
* @author Yaniv Erel
*
* @param <K> Key
* @param <V> Value
*/
public class LRUCache<K, V> {
private static final float hashTableLoadFactor = 0.75f;
private LinkedHashMap<K, V> map;
private int cacheSize;
/**
* Creates a new LRU (last recently used) cache.
*
* @param cacheSize
* the maximum number of entries that will be kept in this cache.
*/
public LRUCache(int cacheSize) {
this.cacheSize = cacheSize;
int hashTableCapacity = (int) Math.ceil(cacheSize / hashTableLoadFactor) + 1;
map = new LinkedHashMap<K, V>(hashTableCapacity, hashTableLoadFactor,
true) { // accessOrder==true
// (an anonymous inner class)
private static final long serialVersionUID = 1;
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > LRUCache.this.cacheSize;
}
};
}
/**
* Retrieves an entry from the cache.<br>
* The retrieved entry becomes the MRU (most recently used) entry.
*
* @param key
* the key whose associated value is to be returned.
* @return the value associated to this key, or null if no value with this
* key exists in the cache.
*/
public synchronized V get(K key) {
return map.get(key);
}
/**
* Adds an entry to this cache. The new entry becomes the MRU (most recently
* used) entry. If an entry with the specified key already exists in the
* cache, it is replaced by the new entry. If the cache is full, the LRU
* (least recently used) entry is removed from the cache.
*
* @param key
* the key with which the specified value is to be associated.
* @param value
* a value to be associated with the specified key.
*/
public synchronized void put(K key, V value) {
map.put(key, value);
}
/**
* Clears the cache.
*/
public synchronized void clear() {
map.clear();
}
/**
* Returns the number of used entries in the cache.
*
* @return the number of entries currently in the cache.
*/
public synchronized int usedEntries() {
return map.size();
}
/**
* Returns a <code>Collection</code> that contains a copy of all cache
* entries.
*
* @return a <code>Collection</code> with a copy of the cache content.
*/
public synchronized Collection<Map.Entry<K, V>> getAll() {
return new ArrayList<Map.Entry<K, V>>(map.entrySet());
}
}
Doubly linked list + Hashtable solution
http://www.programcreek.com/2013/03/leetcode-lru-cache-java/
Friday, November 7, 2014
Wednesday, November 5, 2014
JVM parameters
所有已制定的HotSpot内存管理和垃圾回收算法都基于一个相同的堆内存划分:新生代(young generation)里存储着新分配的和较年轻的对象,老年代(old generation)里存储着长寿的对象。在此之外,永久代(permanent generation)存储着那些需要伴随整个JVM生命周期的对象,比如,已加载的对象的类定义或者String对象内部Cache。
-XX:InitialHeapSize/-Xms & -XX:MaxHeapSize/-Xmx
$ java -XX:InitialHeapSize=128m -XX:MaxHeapSize=2g MyApp
-XX:+HeapDumpOnOutOfMemoryError & -XX:HeapDumpPath
$ java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:OnOutOfMemoryError ="sh ~/cleanup.sh" MyApp
-XX:PermSize and -XX:MaxPermSize
永久代在堆内存中是一块独立的区域,它包含了所有JVM加载的类的对象表示。请注意,这里设置的永久代大小并不会被包括在使用参数-XX:MaxHeapSize 设置的堆内存大小中。
-XX:InitialCodeCacheSize and -XX:ReservedCodeCacheSize
JVM一个有趣的,但往往被忽视的内存区域是“代码缓存”,它是用来存储已编译方法生成的本地代码。
-XX:+UseCodeCacheFlushing
如果代码缓存不断增长,例如,因为热部署引起的内存泄漏,那么提高代码的缓存大小只会延缓其发生溢出。为了避免这种情况的发生,我们可以尝试一个有趣的新参数:当代码缓存被填满时让JVM放弃一些编译代码。不过,我仍建议尽快解决代码缓存问题发生的根本原因,如找出内存泄漏并修复它。
新生代存在的唯一理由是优化垃圾回收(GC)的性能。更具体说,把堆划分为新生代和老年代有2个好处:简化了新对象的分配(只在新生代分配内存),可以更有效的清除不再需要的对象(即死对象)(新生代和老年代使用不同的GC算法)
很多对象的生存时间都很短。同时研究发现,新生对象很少引用生存时间长的对象。结合这2个特点,很明显 GC 会频繁访问新生对象,例如在堆中一个单独的区域,称之为新生代。在新生代中,GC可以快速标记回收”死对象”,而不需要扫描整个Heap中的存活一段时间的”老对象”。
SUN/Oracle 的HotSpot JVM 又把新生代进一步划分为3个区域:一个相对大点的区域,称为”伊甸园区(Eden)”;两个相对小点的区域称为”From 幸存区(survivor)”和”To 幸存区(survivor)”。按照规定,新对象会首先分配在 Eden 中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden 中的对象会被移动到survivor中,直至对象满足一定的年纪(定义为熬过GC的次数),会被移动到老年代。
基于大多数新生对象都会在GC中被收回的假设。新生代的GC 使用复制算法。在GC前To 幸存区(survivor)保持清空,对象保存在 Eden 和 From 幸存区(survivor)中,GC运行时,Eden中的幸存对象被复制到 To 幸存区(survivor)。针对 From 幸存区(survivor)中的幸存对象,会考虑对象年龄,如果年龄没达到阀值(tenuring threshold),对象会被复制到To 幸存区(survivor)。如果达到阀值对象被复制到老年代。复制阶段完成后,Eden 和From 幸存区中只保存死对象,可以视为清空。如果在复制过程中To 幸存区被填满了,剩余的对象会被复制到老年代中。最后 From 幸存区和 To幸存区会调换下名字,在下次GC时,To 幸存区会成为From 幸存区。
总结一下,对象一般出生在Eden区,年轻代GC过程中,对象在2个幸存区之间移动,如果对象存活到适当的年龄,会被移动到老年代。当对象在老年代死亡时,就需要更高级别的GC,更重量级的GC算法(复制算法不适用于老年代,因为没有多余的空间用于复制)
如果新生代过小,会导致新生对象很快就晋升到老年代中,在老年代中对象很难被回收。如果新生代过大,会发生过多的复制过程。我们需要找到一个合适大小,不幸的是,要想获得一个合适的大小,只能通过不断的测试调优。
-XX:NewSize & -XX:MaxNewSize
设置 XX:MaxNewSize 参数时,应该考虑到新生代只是整个堆的一部分,新生代设置的越大,老年代区域就会减少。一般不允许新生代比老年代还大,因为要考虑GC时最坏情况,所有对象都晋升到老年代。(译者:会发生OOM错误) -XX:MaxNewSize 最大可以设置为-Xmx/2
-XX:NewRatio
这种方式的优点是新生代大小会随着整个堆大小动态扩展。例如 -XX:NewRatio=3 指定老年代/新生代为3/1. 老年代占堆大小的 3/4 ,新生代占 1/4
如果针对新生代,同时定义绝对值和相对值,绝对值将起作用。
$ java -XX:NewSize=32m -XX:MaxNewSize=512m -XX:NewRatio=3 MyApp
以上设置, JVM 会尝试为新生代分配四分之一的堆大小,但不会小于32MB或大于521MB
-XX:SurvivorRatio
指定伊甸园区(Eden)与幸存区大小比例. 例如, -XX:SurvivorRatio=10 表示伊甸园区(Eden)是 幸存区To 大小的10倍(也是幸存区From的10倍)
假设幸存区相对伊甸园区(Eden)太小, 相应新生对象的伊甸园区(Eden)永远很大空间, 我们当然希望,如果这些对象在GC时全部被回收,伊甸园区(Eden)被清空,一切正常.然而,如果有一部分对象在GC中幸存下来, 幸存区只有很少空间容纳这些对象.结果大部分幸存对象在一次GC后,就会被转移到老年代 ,这并不是我们希望的.考虑相反情况, 假设幸存区相对伊甸园区(Eden)太大,当然有足够的空间,容纳GC后的幸存对象. 但是过小的伊甸园区(Eden),意味着空间将越快耗尽,增加新生代GC次数,这是不可接受的。
-XX:+PrintTenuringDistribution
指定JVM 在每次新生代GC时,输出幸存区中对象的年龄分布。
-XX:InitialTenuringThreshold, -XX:MaxTenuringThreshold and -XX:TargetSurvivorRatio
-XX:MaxTenuringThreshold=10 -XX:TargetSurvivorRatio=90 设定老年代阀值的上限为10,幸存区空间目标使用率为90%。
有多种方式,设置新生代行为,没有通用准则。我们必须清楚以下2中情况:
1 如果从年龄分布中发现,有很多对象的年龄持续增长,在到达老年代阀值之前。这表示 -XX:MaxTenuringThreshold 设置过大
2 如果 -XX:MaxTenuringThreshold 的值大于1,但是很多对象年龄从未大于1.应该看下幸存区的目标使用率。如果幸存区使用率从未到达,这表示对象都被GC回收,这正是我们想要的。 如果幸存区使用率经常达到,有些年龄超过1的对象被移动到老年代中。这种情况,可以尝试调整幸存区大小或目标使用率。
-XX:+NeverTenure and -XX:+AlwaysTenure
颇为少见的参数,对应2种极端的新生代GC情况.-XX:+NeverTenure , 对象永远不会晋升到老年代.-XX:+AlwaysTenure, 表示没有幸存区,所有对象在第一次GC时,会晋升到老年代。
http://ifeve.com/useful-jvm-flags-part-4-heap-tuning/
http://ifeve.com/useful-jvm-flags-part-5-young-generation-garbage-collection/
-XX:InitialHeapSize/-Xms & -XX:MaxHeapSize/-Xmx
$ java -XX:InitialHeapSize=128m -XX:MaxHeapSize=2g MyApp
-XX:+HeapDumpOnOutOfMemoryError & -XX:HeapDumpPath
$ java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:OnOutOfMemoryError ="sh ~/cleanup.sh" MyApp
-XX:PermSize and -XX:MaxPermSize
永久代在堆内存中是一块独立的区域,它包含了所有JVM加载的类的对象表示。请注意,这里设置的永久代大小并不会被包括在使用参数-XX:MaxHeapSize 设置的堆内存大小中。
-XX:InitialCodeCacheSize and -XX:ReservedCodeCacheSize
JVM一个有趣的,但往往被忽视的内存区域是“代码缓存”,它是用来存储已编译方法生成的本地代码。
-XX:+UseCodeCacheFlushing
如果代码缓存不断增长,例如,因为热部署引起的内存泄漏,那么提高代码的缓存大小只会延缓其发生溢出。为了避免这种情况的发生,我们可以尝试一个有趣的新参数:当代码缓存被填满时让JVM放弃一些编译代码。不过,我仍建议尽快解决代码缓存问题发生的根本原因,如找出内存泄漏并修复它。
新生代存在的唯一理由是优化垃圾回收(GC)的性能。更具体说,把堆划分为新生代和老年代有2个好处:简化了新对象的分配(只在新生代分配内存),可以更有效的清除不再需要的对象(即死对象)(新生代和老年代使用不同的GC算法)
很多对象的生存时间都很短。同时研究发现,新生对象很少引用生存时间长的对象。结合这2个特点,很明显 GC 会频繁访问新生对象,例如在堆中一个单独的区域,称之为新生代。在新生代中,GC可以快速标记回收”死对象”,而不需要扫描整个Heap中的存活一段时间的”老对象”。
SUN/Oracle 的HotSpot JVM 又把新生代进一步划分为3个区域:一个相对大点的区域,称为”伊甸园区(Eden)”;两个相对小点的区域称为”From 幸存区(survivor)”和”To 幸存区(survivor)”。按照规定,新对象会首先分配在 Eden 中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden 中的对象会被移动到survivor中,直至对象满足一定的年纪(定义为熬过GC的次数),会被移动到老年代。
基于大多数新生对象都会在GC中被收回的假设。新生代的GC 使用复制算法。在GC前To 幸存区(survivor)保持清空,对象保存在 Eden 和 From 幸存区(survivor)中,GC运行时,Eden中的幸存对象被复制到 To 幸存区(survivor)。针对 From 幸存区(survivor)中的幸存对象,会考虑对象年龄,如果年龄没达到阀值(tenuring threshold),对象会被复制到To 幸存区(survivor)。如果达到阀值对象被复制到老年代。复制阶段完成后,Eden 和From 幸存区中只保存死对象,可以视为清空。如果在复制过程中To 幸存区被填满了,剩余的对象会被复制到老年代中。最后 From 幸存区和 To幸存区会调换下名字,在下次GC时,To 幸存区会成为From 幸存区。
总结一下,对象一般出生在Eden区,年轻代GC过程中,对象在2个幸存区之间移动,如果对象存活到适当的年龄,会被移动到老年代。当对象在老年代死亡时,就需要更高级别的GC,更重量级的GC算法(复制算法不适用于老年代,因为没有多余的空间用于复制)
如果新生代过小,会导致新生对象很快就晋升到老年代中,在老年代中对象很难被回收。如果新生代过大,会发生过多的复制过程。我们需要找到一个合适大小,不幸的是,要想获得一个合适的大小,只能通过不断的测试调优。
-XX:NewSize & -XX:MaxNewSize
设置 XX:MaxNewSize 参数时,应该考虑到新生代只是整个堆的一部分,新生代设置的越大,老年代区域就会减少。一般不允许新生代比老年代还大,因为要考虑GC时最坏情况,所有对象都晋升到老年代。(译者:会发生OOM错误) -XX:MaxNewSize 最大可以设置为-Xmx/2
-XX:NewRatio
这种方式的优点是新生代大小会随着整个堆大小动态扩展。例如 -XX:NewRatio=3 指定老年代/新生代为3/1. 老年代占堆大小的 3/4 ,新生代占 1/4
如果针对新生代,同时定义绝对值和相对值,绝对值将起作用。
$ java -XX:NewSize=32m -XX:MaxNewSize=512m -XX:NewRatio=3 MyApp
以上设置, JVM 会尝试为新生代分配四分之一的堆大小,但不会小于32MB或大于521MB
-XX:SurvivorRatio
指定伊甸园区(Eden)与幸存区大小比例. 例如, -XX:SurvivorRatio=10 表示伊甸园区(Eden)是 幸存区To 大小的10倍(也是幸存区From的10倍)
假设幸存区相对伊甸园区(Eden)太小, 相应新生对象的伊甸园区(Eden)永远很大空间, 我们当然希望,如果这些对象在GC时全部被回收,伊甸园区(Eden)被清空,一切正常.然而,如果有一部分对象在GC中幸存下来, 幸存区只有很少空间容纳这些对象.结果大部分幸存对象在一次GC后,就会被转移到老年代 ,这并不是我们希望的.考虑相反情况, 假设幸存区相对伊甸园区(Eden)太大,当然有足够的空间,容纳GC后的幸存对象. 但是过小的伊甸园区(Eden),意味着空间将越快耗尽,增加新生代GC次数,这是不可接受的。
-XX:+PrintTenuringDistribution
指定JVM 在每次新生代GC时,输出幸存区中对象的年龄分布。
-XX:InitialTenuringThreshold, -XX:MaxTenuringThreshold and -XX:TargetSurvivorRatio
-XX:MaxTenuringThreshold=10 -XX:TargetSurvivorRatio=90 设定老年代阀值的上限为10,幸存区空间目标使用率为90%。
有多种方式,设置新生代行为,没有通用准则。我们必须清楚以下2中情况:
1 如果从年龄分布中发现,有很多对象的年龄持续增长,在到达老年代阀值之前。这表示 -XX:MaxTenuringThreshold 设置过大
2 如果 -XX:MaxTenuringThreshold 的值大于1,但是很多对象年龄从未大于1.应该看下幸存区的目标使用率。如果幸存区使用率从未到达,这表示对象都被GC回收,这正是我们想要的。 如果幸存区使用率经常达到,有些年龄超过1的对象被移动到老年代中。这种情况,可以尝试调整幸存区大小或目标使用率。
-XX:+NeverTenure and -XX:+AlwaysTenure
颇为少见的参数,对应2种极端的新生代GC情况.-XX:+NeverTenure , 对象永远不会晋升到老年代.-XX:+AlwaysTenure, 表示没有幸存区,所有对象在第一次GC时,会晋升到老年代。
http://ifeve.com/useful-jvm-flags-part-4-heap-tuning/
http://ifeve.com/useful-jvm-flags-part-5-young-generation-garbage-collection/
Subscribe to:
Posts (Atom)