1. GC 설명이 잘 정리된 자료

https://devfunny.tistory.com/681

 

JVM GC (Garbage Collection) 에 대한 정리

가비지 컬렉션 (Garbage Collection) 유효하지 않은 메모리(Garbage)를 자동으로 제거해주는 작업이다. Java Appliation은 JVM(Java Virtual Machine)위에서 구동되는데, JVM의 기능 중 더이상 사용하지 않는 객체를

devfunny.tistory.com

 

2. G1 GC에서 Full GC는 잘 안일어난다.

https://stackoverflow.com/questions/69379494/full-gc-is-not-kicking-in-for-g1-gc-in-java-11-what-could-be-the-reasons

 

Full GC is not kicking in for G1 GC in Java 11. What could be the reasons?

The JVM arguments are as follows: -Xms20g -Xmx20g -XX:MaxGCPauseMillis=10000 -XX:G1ReservePercent=30 -Duser.timezone=UTC The only thing in logs are Pause Young (Normal) (G1 Evacuation Pause) Pause

stackoverflow.com

 

G1에서 결국 Memory 가 Full 단계(To-space exhausted) 까지 가야 Full GC가 발생함.

(원래 Full GC를 피하기 위해서 이런 저런 GC가 나옴)

 

사용자측에서 Memory에 민감해서, 메모리가 40%만 넘어도 민감해 함.

SpringBoot 또는 관련 라이브러리 문제로 Full GC에서만 메모리가 해제되고, 나머지 GC에서는 HashMap 관련 객체가 해제되지 않는 듯 함. (=>  객체를 조사해 보니, bytes는 oracle 데이터, map은 jmx에서 사용, 조정 가능한게 별로 없음)

 

서버 부하가 심하지 않으면, 스케줄러를 이용해서 애플리케이션의 유휴 시간대에 강제로 System.gc() 로 Full GC를 발생하는 것도 방법인 듯. => 결국, 스케줄러에서 새벽에 임계치를 넘으면 초기화 하기로 함.

 

package test;

import java.util.*;

public class GCTest22 {
	public static void main(String[] args) throws InterruptedException {

		final int newMapCount = 1000;
		List<Map> list = new ArrayList<>();
		Map<String, String> newMap = new HashMap<>();
		for (int i = 0; true; i++) {
			if (i > 0 && i % (100 * newMapCount) == 0) {
				long used = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
				System.out.println(getStringWithGreen(String.format(formatMemory, i, used)));
				Thread.sleep(1000);
			}

			if (i > 0 && i % newMapCount == 0) {
				newMap = new HashMap<>();
			}

			String newStr1 = new String(i + "_abcdefghijklmnopqrstuvwxyz1,");
			newMap.put(i + "a", newStr1);

			if (i > newMapCount && (i / newMapCount) % 5 == 0) {
				list.add(newMap);
			}

			if (i > 0 && i % (3000 * newMapCount) == 0) {
				System.gc();
			}
		}
	}

	private static String formatMemory = "테스트 %,d, 메모리사용량: %,d";
	private static String formatGreenColor = "\u001B[32m%s\u001B[0m";

	private static String getStringWithGreen(String s) {
		return String.format(formatGreenColor, s);
	}
}

 

-verbose:gc -Xmx1024m

실행 옵션

 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
	PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<setting name="cacheEnabled" value="false"/>
		<setting name="localCacheScope" value="STATEMENT" /> <!-- STATEMENT / SESSION -->
	</settings>
</configuration>

 

MyBatis에서 localCacheScope(SESSION) 캐시가 해제되지 않아, STATEMENT로 변경했는데 메모리 증가가 둔화는 되었으나 여전히 해당 어플리케이션에서 많은 트랜잭션이 몰리면 메모리 증가 현상이 보임.

=> 이상 없음

 

 

728x90

csv 파일 생성 샘플.

주로 public get 메소드의 값으로 csv 파일을 생성하기 위한 것.

 

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/*
 * Csv 파일 양식 파일을 생성한다.
 */
public class CsvWriter {

	private String csvDelimeter = "\t";
	private String lineSeparator = System.lineSeparator();
	private String methodPrefix = "";
	private Pattern patternCols = null;

	@SuppressWarnings("rawtypes")
	private Class writeClass = Object.class;

	@SuppressWarnings("rawtypes")
	public CsvWriter(Class clz, String csvDelimeter, String lineSeparator, String methodPrefix) {
		this.writeClass = clz;
		this.csvDelimeter = csvDelimeter;
		this.lineSeparator = lineSeparator;
		this.methodPrefix = methodPrefix;
	}

	@SuppressWarnings("rawtypes")
	public CsvWriter(Class clz, String methodPrefix) {
		this.writeClass = clz;
		this.methodPrefix = methodPrefix;
	}

	@SuppressWarnings("rawtypes")
	public CsvWriter(Class clz, String methodPrefix, String regexpIncludeCols) {
		this.writeClass = clz;
		this.methodPrefix = methodPrefix;
		if(regexpIncludeCols != null) {
			this.patternCols = Pattern.compile(regexpIncludeCols, Pattern.CASE_INSENSITIVE); // 대소문자 무시
		}
	}

	/**
	 * Csv 파일 양식 파일을 생성한다.
	 * <pre>
	 * 		String filePath = "C:\\output.csv";
	 *		List<Object> objectList = list.stream()
	 *								.map(m -> (Object)m)
	 *								.collect(Collectors.toList());
	 *		String regexpColsName = "id|name";
	 *		CsvWriter csv = new CsvWriter(클래스.class, "get", regexpColsName);
	 *		csv.writeObjectToCsv(filePath, objectList);
	 * </pre>
	 * @param filePath
	 * @param list
	 */
	public void writeObjectToCsv(String filePath, List<Object> list) {

		List<Method> methodlist = listgetMethod(writeClass);

		StringBuilder sb = new StringBuilder(1000);
		sb.append(getColNames(methodlist, csvDelimeter));
		for(Object lot : list) {
			sb.append(lineSeparator).append(getColValues(methodlist, lot, csvDelimeter));
		}

		// log.info(sb.toString());

		writeCSV(filePath, sb.toString());
	}

	public String getColNames(List<Method> list, String csvDelimeter) {
		StringBuilder sb = new StringBuilder(200);

		for(int i = 0; i < list.size(); i++) {
			if(i > 0)
				sb.append(csvDelimeter);
			sb.append(list.get(i).getName().substring(3)); // get 이후
		}

		return sb.toString();
	}

	public String getColValues(List<Method> list, Object obj, String csvDelimeter) {
		StringBuilder sb = new StringBuilder(200);

		String EMPTY = "";
		for(int i = 0; i < list.size(); i++) {
			if(i > 0)
				sb.append(csvDelimeter);

			try {
				Object returnObj = list.get(i).invoke(obj);
				if(returnObj != null)
					sb.append(returnObj.toString());
				else
					sb.append(EMPTY);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return sb.toString();
	}

	@SuppressWarnings("rawtypes")
	public List<Method> listgetMethod(Class clz) {
		List<Method> list = new ArrayList<>();
		for (Method method : clz.getMethods()) {
			if(!Modifier.isPublic(method.getModifiers())) // public 아니면 skip
				continue;

			if(methodPrefix != null && methodPrefix.length() > 0 && !method.getName().startsWith(methodPrefix)) // prefix와 같지 않으면 skip
				continue;

			if(patternCols != null) {
				String colName = methodPrefix == null? method.getName() : method.getName().substring(methodPrefix.length());
				if(!this.patternCols.matcher(colName).find()) // colume 목록와 매칭되지 않으면 skip
					continue;
			}

			Class returnClass = method.getReturnType();
			if(returnClass.isPrimitive() || method.getReturnType() == String.class || method.getReturnType() == Long.class || method.getReturnType() == Integer.class) {
				list.add(method);
			}
		}

		list = list.stream().sorted(Comparator.comparing(Method::getName)).collect(Collectors.toList());

		return list;
	}

	public void writeCSV(String filePath, String contents) {
		BufferedWriter writer = null;
        try {
        	int BUFFER_SIZE = 1024 * 20;
            FileOutputStream out = new FileOutputStream(new File(filePath));
            writer = new BufferedWriter(new OutputStreamWriter(out, "EUC-KR"), BUFFER_SIZE);
            writer.write(contents);
        } catch (IOException e) {
            throw new IllegalStateException("Can't create writer ", e);
        } finally {
			try {
				if(writer != null)
					writer.close();
			} catch (IOException e) {
			}
        }
	}
}
728x90

 

webflux가 netty 기반이라서, 아직 (Spring5) 기반에서는 request-timeout 설정이 없음.

각 request에서 mono.timout 이 설정이 가능하고,

아니면, 아래와 같이 WebFilter 설정을 통해서 전체 request-timeout 설정이 가능함.

 

그런데, 만약 비동기식으로 처리되고, 서버에서 on-line 배치 방식으로 처리된다면,

아래 WebFilter는 의미가 없을 테고, 해당 Mono에서 timeout 설정을 해야함.

@Slf4j
@Component
public class RequestTimeoutWebFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    	// Request Timeout 설정. WebFlux는 아직 application.yml에서 request-timeout 설정이 없음.
    	// 최근 Tracking 이력 max값 199초. 최대 300초 기준으로 5분으로 산정함.
    	log.info("#################### WebFilter - start");
    	Mono<Void> mono = chain
    			.filter(exchange)
//    			.timeout(Duration.ofMinutes(5)); // 5 분
   		        .timeout(Duration.ofSeconds(10)); // test 
    	log.info("#################### WebFilter - end");
    	return mono;
    }
}

 

end.

 

728x90

+ Recent posts