记一次调优过程

编程

jconsole 远程

增加jmx启动配置

/data/app/jdk1.8.0_151/bin/java
-Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=7018 -Dcom.sun.management.jmxremote.rmi.port=7019
-Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
-jar ./bussiness-0.0.1-SNAPSHOT.jar
-Xms2048m -Xmx2048m -Xmn1024g -Xss2m -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:ParallelGCThreads=4
-XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=3 -XX:+UseParNewGC
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+PrintGCApplicationConcurrentTime -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -Xloggc:../log/gc.log

总结GC统计

jstat -gcutil pid 【间隔时间】 【次数】

Cpu耗时过高

  1. top
    查找到CPU占用率过高的PID
  2. top -Hp PID
     查找到子线程TID
  3. printf "%x

    " TID
    TID 转16进制

  4. jstack -l PID | grep --color=auto -5 TID
    获取JVM指定线程的堆栈信息

具体问题

正则表达式需要预编译

Q

使用过程中,使用了错误的方式处理: 每次都重新生成Pattern

Pattern pattern = Pattern.compile(datePattern1);

Matcher match = pattern.matcher(sDate);

导致系统线程长时间RUNNING在

S

Pattern要定义为static final静态变量,以避免执行多次预编译.

private static final Pattern pattern = Pattern.compile(regexRule);

private void func(...) {

Matcher m = pattern.matcher(content);

if (m.matches()) {

...

}

ArrayList&LinkedList&HashSet 的选择

Q

ArrayList:

public boolean remove(Object o) {

if (o == null) {

for (int index = 0; index < size; index++)

if (elementData[index] == null) {

fastRemove(index);

return true;

}

} else {

for (int index = 0; index < size; index++)

if (o.equals(elementData[index])) {

fastRemove(index);

return true;

}

}

return false;

}

LinkedList:

public boolean remove(Object o) {

if (o == null) {

for (Node<E> x = first; x != null; x = x.next) {

if (x.item == null) {

unlink(x);

return true;

}

}

} else {

for (Node<E> x = first; x != null; x = x.next) {

if (o.equals(x.item)) {

unlink(x);

return true;

}

}

}

return false;

}

发现: 两者在remove指定对象时,都进行了遍历了List。

通过比较源码:ArrayList底层实现是数组;LinkedList则是双向链表(内部类Node封装外部Object)

    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

  1. get(int index): 
           ArrayList直接数组操作性能会好些;
           LinkedList 需要遍历双向链表定位index下的Node。
  2. add(Object o) :
           ArrayList可能要做扩容Arrays.copyOf(核心还是System.arraycopy)。
           LinkedList则直接将原末尾Node的next指针指向新封装Node对象。
  3. add(int index, E element) : 
            ArrayList需要做System.arraycopy操作。
            LinkedList最坏情况下需要遍历查找指定index上的Node做前后指针的重新指向。(最好情况:直接是在队尾,通过判定index = size)
  4. 删除时,
          ArrayList需要做System.arraycopy操作, remove(Object o) 需要提前遍历查找。
          LinkedList无论是remove(int index)还是remove(Object o),都需要遍历双向链表查找指定的Node。但性能应该还是比ArrayList要快。

总结而言:

LinkedList的性能在修改时还是比ArrayList要好些。 但不足以支持当前大数据量的测试要求。

S

选择使用 HashSet!

hashSet的底层实现是通过HashMap来实现hashCode散列定位(hashMap的底层也还是数组,内部有Node来封装外部数据Object)。

    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

    public boolean add(E e) {

return map.put(e, PRESENT)==null;

}

public boolean remove(Object o) {

return map.remove(o)==PRESENT;

}

而hashMap在做查找、put、 remove等操作时,都会先通过对象hashCode计算定位在数组table上的具体存储位置。

    public V put(K key, V value) {

return putVal(hash(key), key, value, false, true);

}

public V remove(Object key) {

Node<K,V> e;

return (e = removeNode(hash(key), key, null, false, true)) == null ?

null : e.value;

}

static final int hash(Object key) {

int h;

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

}

 

以上是 记一次调优过程 的全部内容, 来源链接: utcz.com/z/511438.html

回到顶部