JVM初探-使用堆外内存减少Full GC

标签 : JVM


问题: 大部分主流互联网企业线上Server JVM选用了CMS收集器(如Taobao、LinkedIn、Vdian), 虽然CMS可与用户线程并发GC以降低STW时间, 但它也并非十分完美, 尤其是当出现Concurrent Mode Failure由并行GC转入串行时, 将导致非常长时间的Stop The World(详细可参考JVM初探- 内存分配、GC原理与垃圾收集器).

解决: 由GCIH可以联想到: 将长期存活的对象(如Local Cache)移入堆外内存(off-heap, 又名直接内存/direct-memory), 从而减少CMS管理的对象数量, 以降低Full GC的次数和频率, 达到提高系统响应速度的目的.


引入

这个idea最初来源于TaobaoJVM对OpenJDK定制开发的GCIH部分(详见撒迦的分享-JVM定制改进@淘宝), 其中GCIH就是将CMS Old Heap区的一部分划分出来, 这部分内存虽然还在堆内, 但已不被GC所管理.将长生命周期Java对象放在Java堆外, GC不能管理GCIH内Java对象(GC Invisible Heap):

(图片来源: JVM@Taobao PPT)

  • 这样做有两方面的好处:

    1. 减少GC管理内存:

      由于GCIH会从Old区“切出”一块, 因此导致GC管理区域变小, 可以明显降低GC工作量, 提高GC效率, 降低Full GC STW时间(且由于这部分内存仍属于堆, 因此其访问方式/速度不变- 不必付出序列化/反序列化的开销).
    2. GCIH内容进程间共享:

      由于这部分区域不再是JVM运行时数据的一部分, 因此GCIH内的对象可供对个JVM实例所共享(如一台Server跑多个MR-Job可共享同一份Cache数据), 这样一台Server也就可以跑更多的VM实例.

(实际测试数据/图示可下载撒迦分享PPT).

但是大部分的互联公司不能像阿里这样可以有专门的工程师针对自己的业务特点定制JVM, 因此我们只能”眼馋”GCIH带来的性能提升却无法”享用”. 但通用的JVM开放了接口可直接向操作系统申请堆外内存(ByteBuffer or Unsafe), 而这部分内存也是GC所顾及不到的, 因此我们可用JVM堆外内存来模拟GCIH的功能(但相比GCIH不足的是需要付出serialize/deserialize的开销).


JVM堆外内存

JVM初探 -JVM内存模型一文中介绍的Java运行时数据区域中是找不到堆外内存区域的:



因为它并不是JVM运行时数据区的一部分, 也不是Java虚拟机规范中定义的内存区域, 这部分内存区域直接被操作系统管理.

在JDK 1.4以前, 对这部分内存访问没有光明正大的做法: 只能通过反射拿到Unsafe类, 然后调用allocateMemory()/freeMemory()来申请/释放这块内存. 1.4开始新加入了NIO, 它引入了一种基于Channel与Buffer的I/O方式, 可以使用Native函数库直接分配堆外内存, 然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作, ByteBuffer提供了如下常用方法来跟堆外内存打交道:

API 描述
static ByteBuffer allocateDirect(int capacity) Allocates a new direct byte buffer.
ByteBuffer put(byte b) Relative put method (optional operation).
ByteBuffer put(byte[] src) Relative bulk put method (optional operation).
ByteBuffer putXxx(Xxx value) Relative put method for writing a Char/Double/Float/Int/Long/Short value (optional operation).
ByteBuffer get(byte[] dst) Relative bulk get method.
Xxx getXxx() Relative get method for reading a Char/Double/Float/Int/Long/Short value.
XxxBuffer asXxxBuffer() Creates a view of this byte buffer as a Char/Double/Float/Int/Long/Short buffer.
ByteBuffer asReadOnlyBuffer() Creates a new, read-only byte buffer that shares this buffer’s content.
boolean isDirect() Tells whether or not this byte buffer is direct.
ByteBuffer duplicate() Creates a new byte buffer that shares this buffer’s content.

下面我们就用通用的JDK API来使用堆外内存来实现一个local cache.


示例1.: 使用JDK API实现堆外Cache

注: 主要逻辑都集中在方法invoke()内, 而AbstractAppInvoker是一个自定义的性能测试框架, 在后面会有详细的介绍.

  1. /**
  2. * @author jifang
  3. * @since 2016/12/31 下午6:05.
  4. */
  5. public class DirectByteBufferApp extends AbstractAppInvoker {
  6. @Test
  7. @Override
  8. public void invoke(Object... param) {
  9. Map<String, FeedDO> map = createInHeapMap(SIZE);
  10. // move in off-heap
  11. byte[] bytes = serializer.serialize(map);
  12. ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length);
  13. buffer.put(bytes);
  14. buffer.flip();
  15. // for gc
  16. map = null;
  17. bytes = null;
  18. System.out.println("write down");
  19. // move out from off-heap
  20. byte[] offHeapBytes = new byte[buffer.limit()];
  21. buffer.get(offHeapBytes);
  22. Map<String, FeedDO> deserMap = serializer.deserialize(offHeapBytes);
  23. for (int i = 0; i < SIZE; ++i) {
  24. String key = "key-" + i;
  25. FeedDO feedDO = deserMap.get(key);
  26. checkValid(feedDO);
  27. if (i % 10000 == 0) {
  28. System.out.println("read " + i);
  29. }
  30. }
  31. free(buffer);
  32. }
  33. private Map<String, FeedDO> createInHeapMap(int size) {
  34. long createTime = System.currentTimeMillis();
  35. Map<String, FeedDO> map = new ConcurrentHashMap<>(size);
  36. for (int i = 0; i < size; ++i) {
  37. String key = "key-" + i;
  38. FeedDO value = createFeed(i, key, createTime);
  39. map.put(key, value);
  40. }
  41. return map;
  42. }
  43. }

由JDK提供的堆外内存访问API只能申请到一个类似一维数组的ByteBuffer, JDK并未提供基于堆外内存的实用数据结构实现(如堆外的MapSet), 因此想要实现Cache的功能只能在write()时先将数据put()到一个堆内的HashMap, 然后再将整个Map序列化后MoveInDirectMemory, 取缓存则反之. 由于需要在堆内申请HashMap, 因此可能会导致多次Full GC. 这种方式虽然可以使用堆外内存, 但性能不高、无法发挥堆外内存的优势.

幸运的是开源界的前辈开发了诸如EhcacheMapDBChronicle Map等一系列优秀的堆外内存框架, 使我们可以在使用简洁API访问堆外内存的同时又不损耗额外的性能.

其中又以Ehcache最为强大, 其提供了in-heap、off-heap、on-disk、cluster四级缓存, 且Ehcache企业级产品(BigMemory Max / BigMemory Go)实现的BigMemory也是Java堆外内存领域的先驱.


示例2: MapDB API实现堆外Cache

  1. public class MapDBApp extends AbstractAppInvoker {
  2. private static HTreeMap<String, FeedDO> mapDBCache;
  3. static {
  4. mapDBCache = DBMaker.hashMapSegmentedMemoryDirect()
  5. .expireMaxSize(SIZE)
  6. .make();
  7. }
  8. @Test
  9. @Override
  10. public void invoke(Object... param) {
  11. for (int i = 0; i < SIZE; ++i) {
  12. String key = "key-" + i;
  13. FeedDO feed = createFeed(i, key, System.currentTimeMillis());
  14. mapDBCache.put(key, feed);
  15. }
  16. System.out.println("write down");
  17. for (int i = 0; i < SIZE; ++i) {
  18. String key = "key-" + i;
  19. FeedDO feedDO = mapDBCache.get(key);
  20. checkValid(feedDO);
  21. if (i % 10000 == 0) {
  22. System.out.println("read " + i);
  23. }
  24. }
  25. }
  26. }

结果 & 分析

  • DirectByteBufferApp
  1. S0 S1 E O P YGC YGCT FGC FGCT GCT
  2. 0.00 0.00 5.22 78.57 59.85 19 2.902 13 7.251 10.153
  • the last one jstat of MapDBApp
  1. S0 S1 E O P YGC YGCT FGC FGCT GCT
  2. 0.00 0.03 8.02 0.38 44.46 171 0.238 0 0.000 0.238

运行DirectByteBufferApp.invoke()会发现有看到很多Full GC的产生, 这是因为HashMap需要一个很大的连续数组, Old区很快就会被占满, 因此也就导致频繁Full GC的产生.

而运行MapDBApp.invoke()可以看到有一个DirectMemory持续增长的过程, 但FullGC却一次都没有了.


实验: 使用堆外内存减少Full GC

实验环境

  • java -version
  1. java version "1.7.0_79"
  2. Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
  3. Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)
  • VM Options
  1. -Xmx512M
  2. -XX:MaxDirectMemorySize=512M
  3. -XX:+PrintGC
  4. -XX:+UseConcMarkSweepGC
  5. -XX:+CMSClassUnloadingEnabled
  6. -XX:CMSInitiatingOccupancyFraction=80
  7. -XX:+UseCMSInitiatingOccupancyOnly
  • 实验数据

    170W条动态(FeedDO).

实验代码

第1组: in-heap、affect by GC、no serialize

  • ConcurrentHashMapApp
  1. public class ConcurrentHashMapApp extends AbstractAppInvoker {
  2. private static final Map<String, FeedDO> cache = new ConcurrentHashMap<>();
  3. @Test
  4. @Override
  5. public void invoke(Object... param) {
  6. // write
  7. for (int i = 0; i < SIZE; ++i) {
  8. String key = String.format("key_%s", i);
  9. FeedDO feedDO = createFeed(i, key, System.currentTimeMillis());
  10. cache.put(key, feedDO);
  11. }
  12. System.out.println("write down");
  13. // read
  14. for (int i = 0; i < SIZE; ++i) {
  15. String key = String.format("key_%s", i);
  16. FeedDO feedDO = cache.get(key);
  17. checkValid(feedDO);
  18. if (i % 10000 == 0) {
  19. System.out.println("read " + i);
  20. }
  21. }
  22. }
  23. }

GuavaCacheApp类似, 详细代码可参考完整项目.


第2组: off-heap、not affect by GC、need serialize

  • EhcacheApp
  1. public class EhcacheApp extends AbstractAppInvoker {
  2. private static Cache<String, FeedDO> cache;
  3. static {
  4. ResourcePools resourcePools = ResourcePoolsBuilder.newResourcePoolsBuilder()
  5. .heap(1000, EntryUnit.ENTRIES)
  6. .offheap(480, MemoryUnit.MB)
  7. .build();
  8. CacheConfiguration<String, FeedDO> configuration = CacheConfigurationBuilder
  9. .newCacheConfigurationBuilder(String.class, FeedDO.class, resourcePools)
  10. .build();
  11. cache = CacheManagerBuilder.newCacheManagerBuilder()
  12. .withCache("cacher", configuration)
  13. .build(true)
  14. .getCache("cacher", String.class, FeedDO.class);
  15. }
  16. @Test
  17. @Override
  18. public void invoke(Object... param) {
  19. for (int i = 0; i < SIZE; ++i) {
  20. String key = String.format("key_%s", i);
  21. FeedDO feedDO = createFeed(i, key, System.currentTimeMillis());
  22. cache.put(key, feedDO);
  23. }
  24. System.out.println("write down");
  25. // read
  26. for (int i = 0; i < SIZE; ++i) {
  27. String key = String.format("key_%s", i);
  28. Object o = cache.get(key);
  29. checkValid(o);
  30. if (i % 10000 == 0) {
  31. System.out.println("read " + i);
  32. }
  33. }
  34. }
  35. }

MapDBApp与前同.


第3组: off-process、not affect by GC、serialize、affect by process communication

  • LocalRedisApp
  1. public class LocalRedisApp extends AbstractAppInvoker {
  2. private static final Jedis cache = new Jedis("localhost", 6379);
  3. private static final IObjectSerializer serializer = new Hessian2Serializer();
  4. @Test
  5. @Override
  6. public void invoke(Object... param) {
  7. // write
  8. for (int i = 0; i < SIZE; ++i) {
  9. String key = String.format("key_%s", i);
  10. FeedDO feedDO = createFeed(i, key, System.currentTimeMillis());
  11. byte[] value = serializer.serialize(feedDO);
  12. cache.set(key.getBytes(), value);
  13. if (i % 10000 == 0) {
  14. System.out.println("write " + i);
  15. }
  16. }
  17. System.out.println("write down");
  18. // read
  19. for (int i = 0; i < SIZE; ++i) {
  20. String key = String.format("key_%s", i);
  21. byte[] value = cache.get(key.getBytes());
  22. FeedDO feedDO = serializer.deserialize(value);
  23. checkValid(feedDO);
  24. if (i % 10000 == 0) {
  25. System.out.println("read " + i);
  26. }
  27. }
  28. }
  29. }

RemoteRedisApp类似, 详细代码可参考下面完整项目.


实验结果

* ConcurrentMap Guava
TTC 32166ms/32s 47520ms/47s
Minor C/T 31/1.522 29/1.312
Full C/T 24/23.212 36/41.751
MapDB Ehcache
TTC 40272ms/40s 30814ms/31s
Minor C/T 511/0.557 297/0.430
Full C/T 0/0.000 0/0.000
LocalRedis NetworkRedis
TTC 176382ms/176s 1h+
Minor C/T 421/0.415 -
Full C/T 0/0.000 -

备注:

- TTC: Total Time Cost 总共耗时

- C/T: Count/Time 次数/耗时(seconds)


结果分析

对比前面几组数据, 可以有如下总结:

  • 将长生命周期的大对象(如cache)移出heap可大幅度降低Full GC次数与耗时;
  • 使用off-heap存储对象需要付出serialize/deserialize成本;
  • 将cache放入分布式缓存需要付出进程间通信/网络通信的成本(UNIX Domain/TCP IP)

附:

off-heap的Ehcache能够跑出比in-heap的HashMap/Guava更好的成绩确实是我始料未及的O(∩_∩)O~, 但确实这些数据和堆内存的搭配导致in-heap的Full GC太多了, 当heap堆开大之后就肯定不是这个结果了. 因此在使用堆外内存降低Full GC前, 可以先考虑是否可以将heap开的更大.


附: 性能测试框架

在main函数启动时, 扫描com.vdian.se.apps包下的所有继承了AbstractAppInvoker的类, 然后使用Javassist为每个类生成一个代理对象: 当invoke()方法执行时首先检查他是否标注了@Test注解(在此, 我们借用junit定义好了的注解), 并在执行的前后记录方法执行耗时, 并最终对比每个实现类耗时统计.

  • 依赖
  1. <dependency>
  2. <groupId>org.apache.commons</groupId>
  3. <artifactId>commons-proxy</artifactId>
  4. <version>${commons.proxy.version}</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.javassist</groupId>
  8. <artifactId>javassist</artifactId>
  9. <version>${javassist.version}</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>com.caucho</groupId>
  13. <artifactId>hessian</artifactId>
  14. <version>${hessian.version}</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>com.google.guava</groupId>
  18. <artifactId>guava</artifactId>
  19. <version>${guava.version}</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>junit</groupId>
  23. <artifactId>junit</artifactId>
  24. <version>${junit.version}</version>
  25. </dependency>

启动类: OffHeapStarter

  1. /**
  2. * @author jifang
  3. * @since 2017/1/1 上午10:47.
  4. */
  5. public class OffHeapStarter {
  6. private static final Map<String, Long> STATISTICS_MAP = new HashMap<>();
  7. public static void main(String[] args) throws IOException, IllegalAccessException, InstantiationException {
  8. Set<Class<?>> classes = PackageScanUtil.scanPackage("com.vdian.se.apps");
  9. for (Class<?> clazz : classes) {
  10. AbstractAppInvoker invoker = createProxyInvoker(clazz.newInstance());
  11. invoker.invoke();
  12. //System.gc();
  13. }
  14. System.out.println("********************* statistics **********************");
  15. for (Map.Entry<String, Long> entry : STATISTICS_MAP.entrySet()) {
  16. System.out.println("method [" + entry.getKey() + "] total cost [" + entry.getValue() + "]ms");
  17. }
  18. }
  19. private static AbstractAppInvoker createProxyInvoker(Object invoker) {
  20. ProxyFactory factory = new JavassistProxyFactory();
  21. Class<?> superclass = invoker.getClass().getSuperclass();
  22. Object proxy = factory
  23. .createInterceptorProxy(invoker, new ProfileInterceptor(), new Class[]{superclass});
  24. return (AbstractAppInvoker) proxy;
  25. }
  26. private static class ProfileInterceptor implements Interceptor {
  27. @Override
  28. public Object intercept(Invocation invocation) throws Throwable {
  29. Class<?> clazz = invocation.getProxy().getClass();
  30. Method method = clazz.getMethod(invocation.getMethod().getName(), Object[].class);
  31. Object result = null;
  32. if (method.isAnnotationPresent(Test.class)
  33. && method.getName().equals("invoke")) {
  34. String methodName = String.format("%s.%s", clazz.getSimpleName(), method.getName());
  35. System.out.println("method [" + methodName + "] start invoke");
  36. long start = System.currentTimeMillis();
  37. result = invocation.proceed();
  38. long cost = System.currentTimeMillis() - start;
  39. System.out.println("method [" + methodName + "] total cost [" + cost + "]ms");
  40. STATISTICS_MAP.put(methodName, cost);
  41. }
  42. return result;
  43. }
  44. }
  45. }
  • 包扫描工具: PackageScanUtil
  1. public class PackageScanUtil {
  2. private static final String CLASS_SUFFIX = ".class";
  3. private static final String FILE_PROTOCOL = "file";
  4. public static Set<Class<?>> scanPackage(String packageName) throws IOException {
  5. Set<Class<?>> classes = new HashSet<>();
  6. String packageDir = packageName.replace('.', '/');
  7. Enumeration<URL> packageResources = Thread.currentThread().getContextClassLoader().getResources(packageDir);
  8. while (packageResources.hasMoreElements()) {
  9. URL packageResource = packageResources.nextElement();
  10. String protocol = packageResource.getProtocol();
  11. // 只扫描项目内class
  12. if (FILE_PROTOCOL.equals(protocol)) {
  13. String packageDirPath = URLDecoder.decode(packageResource.getPath(), "UTF-8");
  14. scanProjectPackage(packageName, packageDirPath, classes);
  15. }
  16. }
  17. return classes;
  18. }
  19. private static void scanProjectPackage(String packageName, String packageDirPath, Set<Class<?>> classes) {
  20. File packageDirFile = new File(packageDirPath);
  21. if (packageDirFile.exists() && packageDirFile.isDirectory()) {
  22. File[] subFiles = packageDirFile.listFiles(new FileFilter() {
  23. @Override
  24. public boolean accept(File pathname) {
  25. return pathname.isDirectory() || pathname.getName().endsWith(CLASS_SUFFIX);
  26. }
  27. });
  28. for (File subFile : subFiles) {
  29. if (!subFile.isDirectory()) {
  30. String className = trimClassSuffix(subFile.getName());
  31. String classNameWithPackage = packageName + "." + className;
  32. Class<?> clazz = null;
  33. try {
  34. clazz = Class.forName(classNameWithPackage);
  35. } catch (ClassNotFoundException e) {
  36. // ignore
  37. }
  38. assert clazz != null;
  39. Class<?> superclass = clazz.getSuperclass();
  40. if (superclass == AbstractAppInvoker.class) {
  41. classes.add(clazz);
  42. }
  43. }
  44. }
  45. }
  46. }
  47. // trim .class suffix
  48. private static String trimClassSuffix(String classNameWithSuffix) {
  49. int endIndex = classNameWithSuffix.length() - CLASS_SUFFIX.length();
  50. return classNameWithSuffix.substring(0, endIndex);
  51. }
  52. }

注: 在此仅扫描项目目录下的单层目录的class文件, 功能更强大的包扫描工具可参考Spring源代码或Touch源代码中的PackageScanUtil.


AppInvoker基类: AbstractAppInvoker

提供通用测试参数 & 工具函数.

  1. public abstract class AbstractAppInvoker {
  2. protected static final int SIZE = 170_0000;
  3. protected static final IObjectSerializer serializer = new Hessian2Serializer();
  4. protected static FeedDO createFeed(long id, String userId, long createTime) {
  5. return new FeedDO(id, userId, (int) id, userId + "_" + id, createTime);
  6. }
  7. protected static void free(ByteBuffer byteBuffer) {
  8. if (byteBuffer.isDirect()) {
  9. ((DirectBuffer) byteBuffer).cleaner().clean();
  10. }
  11. }
  12. protected static void checkValid(Object obj) {
  13. if (obj == null) {
  14. throw new RuntimeException("cache invalid");
  15. }
  16. }
  17. protected static void sleep(int time, String beforeMsg) {
  18. if (!Strings.isNullOrEmpty(beforeMsg)) {
  19. System.out.println(beforeMsg);
  20. }
  21. try {
  22. Thread.sleep(time);
  23. } catch (InterruptedException ignored) {
  24. // no op
  25. }
  26. }
  27. /**
  28. * 供子类继承 & 外界调用
  29. *
  30. * @param param
  31. */
  32. public abstract void invoke(Object... param);
  33. }

序列化/反序列化接口与实现

  1. public interface IObjectSerializer {
  2. <T> byte[] serialize(T obj);
  3. <T> T deserialize(byte[] bytes);
  4. }
  1. public class Hessian2Serializer implements IObjectSerializer {
  2. private static final Logger LOGGER = LoggerFactory.getLogger(Hessian2Serializer.class);
  3. @Override
  4. public <T> byte[] serialize(T obj) {
  5. if (obj != null) {
  6. try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
  7. Hessian2Output out = new Hessian2Output(os);
  8. out.writeObject(obj);
  9. out.close();
  10. return os.toByteArray();
  11. } catch (IOException e) {
  12. LOGGER.error("Hessian serialize error ", e);
  13. throw new CacherException(e);
  14. }
  15. }
  16. return null;
  17. }
  18. @SuppressWarnings("unchecked")
  19. @Override
  20. public <T> T deserialize(byte[] bytes) {
  21. if (bytes != null) {
  22. try (ByteArrayInputStream is = new ByteArrayInputStream(bytes)) {
  23. Hessian2Input in = new Hessian2Input(is);
  24. T obj = (T) in.readObject();
  25. in.close();
  26. return obj;
  27. } catch (IOException e) {
  28. LOGGER.error("Hessian deserialize error ", e);
  29. throw new CacherException(e);
  30. }
  31. }
  32. return null;
  33. }
  34. }

完整项目地址: https://github.com/feiqing/off-heap-tester.git.


GC统计工具

  1. #!/bin/bash
  2. pid=`jps | grep $1 | awk '{print $1}'`
  3. jstat -gcutil ${pid} 400 10000
  • 使用

    sh jstat-uti.sh ${u-main-class}

附加: 为什么在实验中in-heap cache的Minor GC那么少?

现在我还不能给出一个确切地分析答案, 有的同学说是因为CMS Full GC会连带一次Minor GC, 而用jstat会直接计入Full GC, 但查看详细的GC日志也并未发现什么端倪. 希望有了解的同学可以在下面评论区可以给我留言, 再次先感谢了O(∩_∩)O~.


JVM初探- 使用堆外内存减少Full GC的更多相关文章

  1. Java堆外内存之七:JVM NativeMemoryTracking 分析堆外内存泄露

    Native Memory Tracking (NMT) 是Hotspot VM用来分析VM内部内存使用情况的一个功能.我们可以利用jcmd(jdk自带)这个工具来访问NMT的数据. NMT介绍 工欲 ...

  2. Spring Boot引起的“堆外内存泄漏”排查及经验总结

    小结: 检索词:C++内存分配器.jvm内存模型.gdb.内存泄露 https://tech.meituan.com/2019/01/03/spring-boot-native-memory-leak ...

  3. 【转载】Spring Boot引起的“堆外内存泄漏”排查及经验总结

    背景 为了更好地实现对项目的管理,我们将组内一个项目迁移到MDP框架(基于Spring Boot),随后我们就发现系统会频繁报出Swap区域使用量过高的异常.笔者被叫去帮忙查看原因,发现配置了4G堆内 ...

  4. spark-调节executor堆外内存

    什么时候需要调节Executor的堆外内存大小? 当出现一下异常时: shuffle file cannot find,executor lost.task lost,out of memory 出现 ...

  5. Spring Boot引起的“堆外内存泄漏”排查及经验总结 strace

    小结: 检索词:C++内存分配器.jvm内存模型.gdb.内存泄露 https://tech.meituan.com/2019/01/03/spring-boot-native-memory-leak ...

  6. JVM堆外内存随笔

    一 JVM堆外内存 1)java与io(file,socket)的操作都需要堆外内存与jvm内存进行互相拷贝,因为操作系统是不懂jvm的内存结构的(jvm的内存结构是自管理的),所以堆外内存存放的是操 ...

  7. Java堆外内存之突破JVM枷锁

    对于有Java开发经验的朋友都知道,Java中不需要手动的申请和释放内存,JVM会自动进行垃圾回收:而使用的内存是由JVM控制的. 那么,什么时机会进行垃圾回收,如何避免过度频繁的垃圾回收?如果JVM ...

  8. JVM - 堆外内存

    看了不少资料,总结下: 堆外内存 / 直接内存(Direct Memory)JDK1.4中引入的NIO类,基于channel和Buffer的I/O方式,可用Native库直接分配堆外内存,然后利用一个 ...

  9. 从内存泄露、内存溢出和堆外内存,JVM优化参数配置参数

    内存泄漏 内存泄漏是指程序在申请内存后,无法释放已申请的内存空间,无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费.内存泄漏最终会导致OOM. 造成内存泄漏 ...

随机推荐

  1. Hibernate(三): org.hibernate.HibernateException: No CurrentSessionContext configured!

    Hibernate版本5.2.9 获取Session的方式是sessionFactory.getCurrentSession(); 比较老一些的版本使用的是sessionFactory.openSes ...

  2. Hive:动静态分区

    http://hugh-wangp.iteye.com/blog/1612268 http://blog.csdn.net/opensure/article/details/46537969 使用静态 ...

  3. 同主机下Docker+nginx+tomcat负载均衡集群搭建

    想用Docker模拟一下nginx+tomcat集群部署,今天折腾了一天,遇坑无数,终于在午夜即将到来之际将整个流程走通,借本文希望给同样遇到类似问题的小伙伴们留点线索. 主机环境是CentOS 7, ...

  4. 解决将/etc/passwd文件中1000改为0后只能guest进入系统的问题

    一, 进入正题之前我先在这里介绍一下vi编辑器的几条主要的编辑命令,以为一会会用的到的.(悔不改当初没好好学unix啊啊啊) 最重要的一点是要知道vi编辑器分为编辑模式和命令模式,按esc键就能从编辑 ...

  5. [LeetCode] Parse Lisp Expression 解析Lisp表达式

    You are given a string expression representing a Lisp-like expression to return the integer value of ...

  6. [LeetCode] Find K Closest Elements 寻找K个最近元素

    Given a sorted array, two integers k and x, find the k closest elements to x in the array. The resul ...

  7. STM32 - GPIO

    买了一个STM32F4的开发板,想把上面的东西重新学一下 快速过: 一.GPIO控制 void GPIO_DeInit(GPIO_TypeDef* GPIOx); //把某一个IO口恢复到默认值 /* ...

  8. [USACO 08DEC]Secret Message

    Description Bessie is leading the cows in an attempt to escape! To do this, the cows are sending sec ...

  9. CodeForces - 724G:Xor-matic Number of the Graph

    两点之间的任意路径都可表示为  随便某一条路径xor任何多个环, 然后可以用线性基来做,这样不会重复的, 另外必须一位一位的处理,xor是不满足结合律的 #include<cstdio> ...

  10. ●HDU 4787 GRE Words Revenge

    题链: http://acm.hdu.edu.cn/showproblem.php?pid=4787 题解: AC自动机(强制在线构造) 题目大意: 有两种操作, 一种为:+S,表示增加模式串S, 另 ...