记Introspector.getBeanInfo()引起的Full GC

背景

  开发环境压力测试,100并发,FullGC频繁,一个转换工具类BeanWithMapHandlerUtil

 

    //把JavaBean转化为map
    public static Map<String, Object> beanToMap(Object bean) throws BaseAppException {

        Map<String, Object> map = new HashMap<>();
        //获取JavaBean的描述器  BeanUtils.describe() <String, String>
        try {
            BeanInfo b = Introspector.getBeanInfo(bean.getClass(), Object.class);
            //获取属性描述器
            PropertyDescriptor[] pds = b.getPropertyDescriptors();
            //对属性迭代
            for (PropertyDescriptor pd : pds) {
                //属性名称
                String propertyName = pd.getName();
                //属性值,用getter方法获取
                Method m = pd.getReadMethod();
                //用对象执行getter方法获得属性值
                Object properValue = m.invoke(bean);
                // 判断是否有 list
                if (properValue instanceof java.util.List) {
                    List list = (List) properValue;

                    if (CollectionUtils.isNotEmpty(list)) {
                        Class childClass = list.get(0).getClass();

                        if (isCommonDataType(childClass) || isWrapClass(childClass)) {
                            map.put(propertyName, list);
                        }
                        else {
                            List<Map<String, Object>> mapList = new ArrayList<Map<String, Object>>();
                            Object obj;
                            for (int i = 0; i < list.size(); i++) {
                                obj = list.get(i);
                                // list里是map或String,不会存在list里直接是list的
                                Field[] fieldChilds = obj.getClass().getDeclaredFields();
                                Map<String, Object> resultChild = new HashMap<String, Object>();
                                for (Field field : fieldChilds) {
                                    // 重置属性可见(而且一般属性都是私有的),否则操作无效
                                    boolean accessible2 = field.isAccessible();
                                    if (!accessible2) {
                                        field.setAccessible(true);
                                    }
                                    // 获取属性名称及值存入Map
                                    String key = field.getName();
                                    Object objVal = field.get(obj);
                                    if (null != objVal && !"".equals(objVal)) {
                                        resultChild.put(key, field.get(obj));
                                    }
                                }
                                mapList.add(resultChild);
                            }
                            map.put(propertyName, mapList);
                        }
                    }
                }
                else {
                    //把属性名-属性值 存到Map中
                    if (null != properValue && !"".equals(properValue)) {
                        map.put(propertyName, properValue);
                    }
                }
            }
        }
        catch (IllegalAccessException e) {
            throw  new BaseAppException("beanToMap IllegalAccessException {}", e);
        }
        catch (IntrospectionException e) {
            throw  new BaseAppException("beanToMap IntrospectionException {}", e);
        }
        catch (InvocationTargetException e) {
            throw  new BaseAppException("beanToMap InvocationTargetException {}", e);
        }
        return map;
    }

  通过thread -n 10 查看线程堆栈信息,线程处在阻塞状态

原因

"http-nio-8080-exec-44" Id=499 cpuUsage=3% BLOCKED on java.lang.Object@53793dcd owned by "http-nio-8080-exec-185" Id=640
    at java.lang.ClassLoader.loadClass(ClassLoader.java:404)
    -  blocked on java.lang.Object@53793dcd
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at com.sun.beans.finder.ClassFinder.findClass(ClassFinder.java:103)
    at java.beans.Introspector.findCustomizerClass(Introspector.java:1301)
    at java.beans.Introspector.getTargetBeanDescriptor(Introspector.java:1295)
    at java.beans.Introspector.getBeanInfo(Introspector.java:425)
    at java.beans.Introspector.getBeanInfo(Introspector.java:262)
    at java.beans.Introspector.getBeanInfo(Introspector.java:224)
            BeanInfo b = Introspector.getBeanInfo(bean.getClass(), Object.class);

  可以看到java.lang.ClassLoader#loadClass(java.lang.String, boolean)中有一把synchronized锁,请求DTO转化成Map,并行变串行

解决方案

Map<String, Object> reqMa = JsonUtil.json2Map(JsonUtil.object2Json(bean));

Object转Map换成JsonUtil工具类,验证如下:

public static void main(String[] args) {

        RestInvocationDto req = new RestInvocationDto();
        req.setServer("test");
        req.setReqParams("test");
        req.setConfigServerKey("test");
        req.setBusinessUrl("test");

        Thread threadA = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                Long begin = System.currentTimeMillis();
                for (int i = 0; i < 1000; i++) {
                    Map<String, Object> reqMap = BeanWithMapHandlerUtil.beanToMap(req);
                }
                System.out.println("ThreadA costs " + (System.currentTimeMillis() - begin) + "ms");
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                Long begin = System.currentTimeMillis();
                for (int i = 0; i < 1000; i++) {
                    JsonUtil.json2Map(JsonUtil.object2Json(req));
                }
                System.out.println("ThreadB costs " + (System.currentTimeMillis() - begin) + "ms");
            }
        });

        threadA.start();
        threadB.start();
    }

ThreadB costs 2321ms
ThreadA costs 8312ms

 

posted on 2021-09-28 15:34  胡子就不刮  阅读(359)  评论(0编辑  收藏  举报

导航