1.Algorithm:https://leetcode.com/problems/maximum-product-subarray/

求最大子序列乘积。

方法1 递归遍历每一种子串的序列,每次计算保留最大值在函数中传递。遍历完成得到最大值。

方法2 dp。dp,因为无法确定下一个访问到的是正数还是负数,所以dp方程是二维的,需要记录一个最大值和一个最小值。如果遍历下一个数是负数,用最小值与相乘,如果是正数,则用最大值相乘。同时记录一个最大值,数组遍历完成得到最大值。

2.Review:https://shipilev.net/jvm/anatomy-quarks/5-tlabs-and-heap-parsability/

TLAB与堆可分析性

在 GC 理论中,可靠的收集器需要维护一个重要的属性——堆的可解析性,也就是将堆塑造成可以解析对象、字段等的形式,而且不需要复杂的元数据的支持。如果堆是可解析的,那么我们可以假设已经分配的堆从头到尾是一个连续的对象流。严格来说这并不是一个必要属性,但是这个属性使得 GC 实现、测试和调试更简单。

由于TLAB,我们无法精确的知道TLAB内部内存是如何分配的。所以作者设计了一个实验,在多个TLAB填充数据,分析最后的dump文件来观察TLAB内部的内存分配情况

...........|===================           ]............
            ^                  ^           ^
        TLAB start        TLAB used   TLAB end

我们可以停止线程,然后请求线程在 TLAB 的空闲部分中分配填充对象,使得这部分堆内存可解析:

 ...........|===================!!!!!!!!!!!]............
            ^                  ^           ^
        TLAB start        TLAB used   TLAB end

实验代码如下:

起很多线程分配自己的 TLAB,然后主线程持续分配对象耗尽 Java 堆内存,最终程序将会以 OutOfMemoryException 崩溃,这会触发堆转储(heap dump)。

import java.util.*;
import java.util.concurrent.*;

public class Fillers {
  public static void main(String... args) throws Exception {
    final int TRAKTORISTOV = 300;
    CountDownLatch cdl = new CountDownLatch(TRAKTORISTOV);
    for (int t = 0 ; t < TRAKTORISTOV; t++) {
      new Thread(() -> allocateAndWait(cdl)).start();
    }
    cdl.await();
    List<Object> l = new ArrayList<>();
    new Thread(() -> allocateAndDie(l)).start();
  }

  public static void allocateAndWait(CountDownLatch cdl) {
    Object o = new Object();  // Request a TLAB
    cdl.countDown();
    while (true) {
      try {
        Thread.sleep(1000);
      } catch (Exception e) {
        break;
      }
    }
    System.out.println(o); // Use the object
  }

  public static void allocateAndDie(Collection<Object> c) {
    while (true) {
      c.add(new Object());
    }
  }
}

为了控制 TLAB 的大小,我们继续使用 Epsilon GC。启动参数为 -Xmx1G -Xms1G -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+HeapDumpOnOutOfMemoryError,这样程序将会很快崩溃,并且输出堆转储。

在 Eclipse Memory Analyzer (MAT) 中打开这个堆转储——我非常喜欢这个工具——我们可以看到下述类直方图:

Class Name                                 |   Objects | Shallow Heap |
-----------------------------------------------------------------------
                                           |           |              |
int[]                                      |     1,099 |  814,643,272 |
java.lang.Object                           | 9,181,912 |  146,910,592 |
java.lang.Object[]                         |     1,521 |  110,855,376 |
byte[]                                     |     6,928 |      348,896 |
java.lang.String                           |     5,840 |      140,160 |
java.util.HashMap$Node                     |     1,696 |       54,272 |
java.util.concurrent.ConcurrentHashMap$Node|     1,331 |       42,592 |
java.util.HashMap$Node[]                   |       413 |       42,032 |
char[]                                     |        50 |       37,432 |
-----------------------------------------------------------------------

int[] 消耗了最多堆内存!这是我们的填充对象。当然,这个实验有一些注意事项。

首先,我们配置 Epsilon 具有静态的 TLAB 大小。高性能的收集器将会自适应 TLAB 的大小,当线程已经分配了一些对象,但是仍然占用很多 TLAB 内存的时候,这种自适应的机制将会最小化无效内存的占用。这也是不要设置太大 TLAB 的一个原因。如果设置了较大的 TLAB ,那么在一个持续分配对象的线程中仍然可能观察到填充对象,但是这并不是真正的对象。

然后,我们通过配置 MAT 来展示不可达的对象。从定义上来说,填充对象是不可达的。它们出现在堆转储中仅仅是堆可解析属性的副作用。这些对象并不是真的存在,一个成熟的堆转储分析器将会为你过滤掉这些对象——这也就是 900 MB 对象就能耗尽 1G 堆内存的一个原因。

在网上找到一篇译文,翻参考

https://www.jianshu.com/p/22f041895d01

3.Tip:跟着网络编程实战专栏,复习了一下select,poll,epoll。linux网络编程必备底层的知识。

4.share: https://thurstonzk2008.com/2019/09/24/%e5%8d%95%e5%85%83%e5%8c%96%e7%9a%84%e4%b8%80%e4%ba%9b%e9%97%ae%e9%a2%98%e7%9a%84%e6%80%bb%e7%bb%93/