H2 버전 조정

	<properties>
		<java.version>11</java.version>
		<!-- 2.x 최신버전에서 validation이 강화되어, 버전을 낮춰서 테스트 진행하는 것으로 보임. JJH -->
		<h2.version>1.4.196</h2.version>
	</properties>

1. 1H2 File DB 및 Memory DB 설정

2. 초기 schema(DDL) 및 data(DML) 설정

spring:
  datasource:
    # 1. H2 file DB. 어플리케이션 경로에서 생성. DB경로. AUTO_SERVER는 다중접속여부
    url:  jdbc:h2:file:./db/h2/data;AUTO_SERVER=true
    # 2. H2 file DB. ~ 는 사용자 계정아래에 DB 생성.
#    url:  jdbc:h2:~/db/h2/data;AUTO_SERVER=true
    # 3. H2 Memory DB
#    url:  jdbc:h2:mem:testdb
    driverClassName: org.h2.Driver
    username: sa
    password:
  h2:
    console:
      enabled: true
      path: /h2-console
  sql:
    init:
      mode: always
      schema-locations: classpath:/h2/schema.sql
      data-locations: classpath:/h2/data.sql
#  jpa:
#    defer-datasource-initialization: true

 

 

728x90

'Tools > Spring' 카테고리의 다른 글

[펌] [Spring boot] 분산 트랜젝션  (1) 2025.02.20
Spring 관련 한글 문서  (0) 2023.10.06
spring application.properties 정보  (0) 2023.08.18
J2DBC 참조  (0) 2023.05.09
web Flux 참조  (0) 2023.05.09

 

https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html

 

Common Application Properties

 

docs.spring.io

 

end.

 

728x90

'Tools > Spring' 카테고리의 다른 글

[펌] [Spring boot] 분산 트랜젝션  (1) 2025.02.20
Spring 관련 한글 문서  (0) 2023.10.06
[SpringBoot 2.7.*] H2 Jdbc 설정, schema(DDL) 및 data(DML)  (0) 2023.09.16
J2DBC 참조  (0) 2023.05.09
web Flux 참조  (0) 2023.05.09

 

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

+ Recent posts