반응형
반응형
<!-- 로딩 -->
<div id="fade" class="black_background"></div>
<div id="light" class="white_content">
	<img src="<%=request.getContextPath()%>/resources/image/loading.gif"
		style="width: 100px; height: 100px;"> <span
		style="margin: 0 0 0 10px;">진행 중입니다.</span>
</div>
<!-- 로딩 -->​
.black_background { 
    position: fixed; /* fixed를 사용하여 스크롤에 따라 따라다니지 않도록 합니다. */
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background: #666666 ;
    z-index:1;
    opacity:.50;
} 

.white_content { 
	display: none; 
	position: absolute; 
	padding: 16px; 
	width:350px; 
	border: 16px solid #E3F2FC; 
	background-color: white; 
	z-index:9999; 
	overflow: auto; 
}
function wrapWindowByMask() {
	/** 화면의 높이와 너비를 구한다. **/
	var maskHeight = $(document).height();
	var maskWidth = $(document).width();
	/** 마스크의 높이와 너비를 화면 것으로 만들어 전체 화면을 채운다. **/
	$('#fade').css({ 'width': maskWidth, 'height': maskHeight });

}

/// 화면의 중앙에 레이어띄움 
function showLayer() {
	wrapWindowByMask();

	$("#light").css("position", "absolute");
	$("#light").css("top", Math.max(0, (($(window).height() - $("#light").outerHeight()) / 2) + $(window).scrollTop() - 100) + "px");
	$("#light").css("left", Math.max(0, (($(window).width() - $("#light").outerWidth()) / 2) + $(window).scrollLeft()) + "px");
	$('#fade').show(); $('#light').show();
} 

function close() {
	$('#fade').hide(); $('#light').hide();
}

 

반응형
반응형

📝프론트엔드 개발자

주로 HTML, CSS, JavaScript 등을 사용하여 웹 페이지나 애플리케이션의 시각적인 부분을 담당합니다

백엔드로 부터 데이터를 받아와 데이터를 보여주는 등의 역할

 

📝백엔드(서버쪽)

데이터베이스와의 상호작용, 서버 로직 구현 등을 담당하여 프론트엔드가 필요로 하는 데이터를 처리하고 전달

 

📝모바일개발자

Android나 iOS 플랫폼에 맞게 앱을 디자인하고 개발

 

📝애플리케이션개발자

다양한 플랫폼에 적용되는 소프트웨어 애플리케이션을 개발응용 프로그램, 모바일 앱 등

 

📝게이밍 플레이 개발자

게임의 사용자 경험을 개선하고 다양한 게임 요소를 설계하며 게임 플레이를 개발모든 환경 움직임 코드로 구현

 

📝게이밍 엔진 개발자

게임 엔진을 개발하거나 기존 엔진을 수정하여 게임의 핵심 기능을 구현물리 엔진, 그래픽스, 사운드 처리 등에 전문화

 

📝보안 개발자

소프트웨어와 네트워크 시스템의 보안을 담당

 

📝백엔드 시스템 플랫폼 개발자

대규모 시스템의 백엔드 인프라를 개발하고 관리합니다 → 분산 시스템, 클라우드 서비스, 데이터베이스 관리 등에 전문화

반응형
반응형

📝SGML 

<QUOTE TYPE="example">
	typically something like <ITALICS>this</ITALICS>
</QUOTE>
<!-- <QUOTE>, <ITALICS>을 의미 -->

다양한 마크업 언어를 만들 수 있는 범용 메타(추가적인 정보) 언어로 매우 복잡하며 사용자 정의 언어를 만들 수 있는 유연한 구조를 가지고 있다 → SGML에서 파생된 건 HTML, XML이 존재

 

📝XML

<?xml version="1.0" encoding="UTF-8"?>
<addressBook>
  <contact>
    <name>John Doe</name>
    <email>john.doe@example.com</email>
    <phone>123-456-7890</phone>
  </contact>
  <contact>
    <name>Jane Smith</name>
    <email>jane.smith@example.com</email>
    <phone>987-654-3210</phone>
  </contact>
</addressBook>

SGML에서 파생된 언어SGML보다 간단하며 미리 정의된 규칙에 따라 작성을 해야한다 유효성 검사를 위해 DTD 또는 XML 스키마를 사용한다

 

📝DTD

XML에서는 자유롭게 태그를 정의하여 문서를 작성 할 수 있지만, 어떤 태그를 사용할지 XML 문서의 구조를 결정 해두면 유용하다. XML은 구조를 작성할 때 DTD라는 언어를 사용한다.

 

XML 태그를 마음대로 정할 수 있지만 이러한 형태로 써라라는 걸 선언한 경우 더 효율적이다.

(서로 관계가 있는 매장하고 거래를 할 때 데이터 이름을 어떻게 할지에 대한 프로토콜 정의)

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE 재고정보[
    <!ELEMENT 재고정보 (상품)>
    <!ELEMENT 상품 (이름, 개수)>
    <!ELEMENT 이름 (#PCDATA)>
    <!ELEMENT 개수 (#PCDATA)>
]>

<재고정보>
    <상품>
        <이름>책장</이름>
        <개수>12</개수>
    </상품>
</재고정보>

 

 

🔗 참고 및 출처
https://araikuma.tistory.com/769

 

반응형
반응형

📝서버

서버는 클라이언트에게 네트워크를 통해 정보나 서비스를 제공하는 컴퓨터입니다.

레이어 7계층을 다 사용할 수 있습니다 → 즉, L2, L3, L4, 스위칭 등 다 만들 수 있습니다.

 

📝DNS 플러시 

캐싱이라는 것으로 한번 들어간 서버의 DNS 정보를 가지고 있어서 빠르게 다시 접속할 수 있게 도와줍니다 → 도메인이름으로 DNS서버를 거쳐서 아이피 정보를 가져오는 작업을 안 해도 됨

하지만 서버가 이전될 시 DNS가 변경되기 때문에 캐싱을 강제로 초기화(DNS 플러시)를 하지 않으면 옛날 아이피로 계속 들어갈 수 있습니다. 그래서 서버 이전시에 해당 작업이 필요합니다.

 

📝웹 방화벽

웹 애플리케이션 보안에 특화되어 개발된 솔루션입니다. 웹방화벽의 기본 역할은 그 이름에서도 알 수 있듯, SQL Injection, Cross-Site Scripting(XSS)등과 같은 웹 공격을 탐지하고 차단하는 것입니다. 

 

📝방화벽

일반적인 방화벽은 모든 포트를 차단하여 외부에서 내부 시스템으로의 불법 접근을 막습니다

네트워크 보안을 강화하기 위한 핵심적인 보안 장치 중 하나

 

📝IDS (Intrusion Detection System)

네트워크나 시스템에서 발생하는 이상 행위를 감지하여 경고를 발생시키는 시스템 → 침입탐지시스템

 

📝IPS (Intrusion Prevention System)

IPS(침입방지시스템)는 IDS와 비슷하지만, 이상 행위를 탐지하는 것뿐만 아니라 실시간으로 침입을 방지하도록 설계

 






 

 

반응형
반응형

📝개발서버

말 그대로 개발하기 위한 서버입니다.

 

📝스테이징서버

개발은 계속 이루어져야하고 버전별로 운영서버로 배포하기 전에 두는 서버입니다.

 

📝운영서버(리얼서버)

실제 운영되는 서버로서 사용자들이 사용하는 서버입니다.

 

📝정적바인딩

실행 이전에 값이 확정되는 경우로 컴파일 타임에 호출될 함수가 결정된다 → Final과 같은 고정 상수를 의미한다 즉, 바뀌지 않으며 무조건 고정적인 값을 의미
정적 바인딩은 컴파일 시 이미 값이 확정되어있기 때문에 실행시 속도 효율이 높아진다

 

📝동적바인딩

실행 이후에 값이 확정되면 동적 바인딩이라 한다 → 로직을 통해 A라는 사용자는 C라는 결과가 B라는 사용자는 D라는 결과가 이런식으로 바뀌는 걸 의미한다

코드 한개로 다양한 경우에 처리가 가능하지만 그냥 무지성으로 코딩하면 메모리 공간 낭비, 속도 이슈 등이 있을 수 있다

 

📝HR (인적자원관리)

조직의 목표를 이루기 위해 필요한 인적 자원을 확보·개발·활용하는 활동을 계획하고 관리하는 일련의 과정이다

 

📝그룹웨어

협업 시스템으로 연차관리, 일정관리 등.. 업무 효율을 높여주는 사내 사이트를 의미한다.

 

📝WBS

프로젝트의 전체 일정수립 및 관리 문서​이다.

 

📝ISP

인터넷을 제공하는 공급자란 의미로 국내 기업에는 KT, SKT 등이 있습니다 또한 밖에서 사용자들이 이용할 수 있는 서버를 임대할 때 클래스개념으로 ISP에서 할당해줍니다.

반응형
반응형

📝프롬프트, 명령프롬프트(cmd)

컴퓨터가 입력을 기다리고 있음을 나타내는 표시

명령프롬프트(cmd) → 명령을 기다리고 있다

 

📝CLI (Command line interface)

명령 줄 인터페이스 [대화식인터프리터]로 키보드로만 써서 다 할 수 있다
cmd를 통해서 작업하는 방식을 생각하면 이해하기 쉽다

 

📝GUI

사용자가 컴퓨터와 정보를 교환할 때 그래픽을 통해 작업할 수 있는 환경을 말한다 → 예시) 아이콘
마우스 등을 이용하여 화면에 있는 메뉴를 선택하여 작업을 할 수 있다

 

📝Compilation 플랫폼 의존적

OS별로 컴파일 방법이 다 다르기 때문에 OS에 따라 실행할 수도 있고 못할 수도 있다 → 기계어가 다름

 

📝Interpretation 플랫폼 독립적 

Compilation의 경우 OS별로 컴파일 방법이 다 다르지만 Interpretation 플랫폼의 독립적이면 어떤 중간 장치(Java의 경우 JVM이 될 수 있다) 각 OS에 맞게 변환 해 컴파일을 해준다

 

📝Kotline

JetBrain을 만든 회사가 만들었으며 JVM에서 동작하는 크로스 플랫폼 오픈소스 프로그래밍 언어로 자바와 호환이 가능하고 서로의 코드로 변환 시키는게 가능하다. → 안드로이드 스튜디오를 이용해 코틀린 자료를 자바로 변환하여 사용가능하다
코틀린과 스프링을 접목시킨 코프링이라는 것도 존재한다.

  • Null Safe (Null에 대한 자동 처리)
  • corouting (많은 걸 한번에 실행가능한 코드 생성 가능) → 실제 써먹을 정도인지는 안 해봐서 모름
  • 코틀린 → JS로 컴파일 (Node.js, React.js와 호환 가능) → 실제 써먹을 정도인지는 안 해봐서 모름

 

📝exe 파일 장점

exe 파일은 실행 파일로, 컴파일된 코드를 포함하며 소스 코드를 숨기고 보호하는 기능

반응형
반응형
public static String httpsGet(String strURL) throws Exception
{
    URL url = null;
    HttpsURLConnection con = null;
    String ret = new String();

    try {
        url = new URL(strURL);
        ignoreSsl();
        con = (HttpsURLConnection)url.openConnection();


        BufferedReader br = null;
        br = new BufferedReader(new InputStreamReader(con.getInputStream()));

        String input = null;

        while ((input = br.readLine()) != null){
            ret += input;
        }

        br.close();
    }
    catch (IOException e) {
        ExceptionUtil.getStackTrace(e);
    } finally {
        if (con != null) {
            con.disconnect();
        }
    }

    return ret;

}

 

 

public static void ignoreSsl() throws Exception{
    HostnameVerifier hv = new HostnameVerifier() {
    public boolean verify(String urlHostName, SSLSession session) 
            return true;
        }
    };
    trustAllHttpsCertificates();
    HttpsURLConnection.setDefaultHostnameVerifier(hv);
}

private static void trustAllHttpsCertificates() throws Exception {
    TrustManager[] trustAllCerts = new TrustManager[1];
    TrustManager tm = new miTM();
    trustAllCerts[0] = tm;
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, null);
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}

static class miTM implements TrustManager,X509TrustManager {
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }

    public boolean isServerTrusted(X509Certificate[] certs) {
        return true;
    }

    public boolean isClientTrusted(X509Certificate[] certs) {
        return true;
    }

    public void checkServerTrusted(X509Certificate[] certs, String authType)
            throws CertificateException {
        return;
    }

    public void checkClientTrusted(X509Certificate[] certs, String authType)
            throws CertificateException {
        return;
    }
}

 

 

출처 : https://ram2ram2.tistory.com/16

반응형
반응형

📝AES

대칭키 알고리즘으로 "대칭키" 하나로 암호화 및 복호화 모두 진행하고 문자열 길이에 제약이 없다.

 

📝AES 암호화 키 방식

  • AES128
    • 암호화 키를 128bit 즉 16byte로 설정한다.
  • AES192
    • 암호화 키를 192bit 즉 24byte로 설정한다.
  • AES256
    • 암호화 키를 256bit 즉 32byte로 설정한다.

 

📝Secret Key

Secret Key는 평문을 암호화하는데 사용되며 절대로 외부에 노출되어서는 안됩니다. AES128, AES192, AES256에 해당하는 암호화키입니다.

 

📝AES 암호화 방식

  • ECB
    • 평문을 일정 크기의 블록으로 잘라 각 블록을 암호화시키는데 암호화와 복호화에 사용되는 키가 동일하기 때문에 유추가능할 경우 보안에 취약하다
    • 예) ABC를 암호화 시켜서 123이 나오면 DABC를 했을땐 4123으로 유추가 가능

 

 

  • CBC
    • ECB의 보안 취약을 보완한 방식으로 보안성이 제일 높은 암호화 방법으로 가장 많이 사용한다
    • IV라는 추가적인 키를 사용하며 IV키로 1차 암호화 후 2차로  Secret Key로 암호화하는 방식이다. 일반적으로 IV키는 고정적으로 하지 않으며 랜덤한 값으로 하는게 좋다

 

 

📝PKCS5 Padding

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.lang.RandomStringUtils;

public class Test {
	

	/** ───── IV키 생성 설정 ───── **/
	private final static int BYTE_LENGTH = 16;
	private final static boolean USE_LETTERS = true;
	private final static boolean USE_NUMBERS = false;
	
	/** ───── 암호화키 바이트 종류 ───── **/
	private final static String SECRET_KEY_128 = "aeskey1234567898";                 // 16byte == 128bit
	private final static String SECRET_KEY_192 = "aeskey12345678987654321a";         // 24byte == 192bit
	private final static String SECRET_KEY_256 = "aeskey12345678987654321asekey987"; // 32byte == 256bit
	
	
	private final static String SECRET_KEY = SECRET_KEY_256; // 32byte == 256bit
	private final static String IV = RandomStringUtils.random(BYTE_LENGTH, USE_LETTERS, USE_NUMBERS); // 랜덤 IV값
	private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding"; // AES 암호화 알고리즘의 CBC 방식을 채용하고 비는 바이트는 PKCS5Padding 방식을 채용

	
	public static void main(String[] args) throws java.io.IOException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
	 
		String password = "암호화 해주세요~";
		String encryptedPassword = encrypt(password);
		String decryptedPassword = decrypt(encryptedPassword);
			
		System.out.println("password : " +  password);
		System.out.println("encryptedPassword : " +  encryptedPassword);
		System.out.println("decryptedPassword : " +  decryptedPassword);
	
	}


	/** ───── 암호화 ───── **/
	public static String encrypt(String passwordToEncrypt) throws NoSuchPaddingException, NoSuchAlgorithmException,
         InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

		Cipher cipher = Cipher.getInstance(TRANSFORMATION);
		SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY.getBytes(), "AES");
		IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8));

		cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);

		return Base64.getEncoder().encodeToString(cipher.doFinal(passwordToEncrypt.getBytes(StandardCharsets.UTF_8)));
   }

	/** ───── 복호화 ───── **/
	public static String decrypt(String passwordToDecrypt) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
         InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
	   
	   Cipher cipher = Cipher.getInstance(TRANSFORMATION);
	   SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY.getBytes(), "AES");
	   IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8));

	   cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);

	   return new String(cipher.doFinal(Base64.getDecoder().decode(passwordToDecrypt)), "UTF-8");
   }
}

암호화하려는 데이터가 블록사이즈를 채우지 못한 경우 부족한 길이만큼 패딩을 하는 방식이다 예) A로 끝나고 8바이트의 블록사이즈라면 7바이트를 공백으로 채우는 방식

 

 

🔗 참고 및 출처

https://veneas.tistory.com/entry/JAVA-%EC%9E%90%EB%B0%94-AES-%EC%95%94%ED%98%B8%ED%99%94-%ED%95%98%EA%B8%B0-AES-128-AES-192-AES-256
https://velog.io/@reedfoxy/CBC-%EB%B8%94%EB%A1%9D-%EC%95%94%ED%98%B8%ED%99%94IV-Initialization-Vector-%EC%82%AC%EC%9A%A9-%EC%A0%84%EB%9E%B5

반응형
반응형

 

출처 : https://coding-factory.tistory.com/830

 

메소드(Method) 영역

Static 영역이라고도 하며 전역 변수와 정적 멤버변수(static 변수)가 저장되는 영역입니다.

공통으로 사용 가능한 영역이라 Static 영역에는 다 같이 쓰이는 객체를 선언하면 좋습니다.

Static 영역의 데이터는 프로그램의 시작부터 종료가 될 때까지 메모리에 남아있게 됩니다.

예) 사용자 50명이 주소검색 API를 요청할 때 주소API코드 = “000000001“ 공통적인 부분이 쓰일 경우 50명의 객체를 만들어서 힙 메모리에 쌓이게 하는 방법보다는 공통적으로 다 사용하는 메소드 영역에 선언하는게 더 효율적이다.

 

스택(Stack) 영역

지역변수, 인자값, 리턴값저장되는 영역이고 메소드 안에서 사용되는 기본형 변수들이 값과 함께 저장되고 Heap 영역에 생성된 객체들을 참조하는 주소값이 할당됩니다.

예) String name = “Annna” 일 경우 name 이라는 변수가 힙 영역에 생성된 Annna라는 값을 참조하게 됩니다.

 

 

힙(Heap) 영역

자바 프로그램에서 사용되는 모든 인스턴스 변수(객체)들이 저장되는 영역입니다.

힙 영역은 메모리 공간이 동적으로 할당되고 해제되며 메모리의 낮은 주소에서부터 높은 주소로 할당이 이루어집니다

예) Annna라는 값은 여기에 생성되게 됩니다.

 

출처 : https://pjh3749.tistory.com/255

 

String 은 매우 자주 쓰이게 때문에 특별 대우를 받습니다.

String의 특징으로는 불변의 값을 넣는게 좋습니다.

문자열 리터럴은 공통 풀(공통 영역)에 공유 데이터로 저장 되기 때문에 불변의 값을 넣도록 설계 되어 있습니다.

 

String s1 = "Hello"; // String literal

String s2 = "Hello"; // String literal

String s3 = s1; // 같은 참조

String s4 = new String("Hello"); // String object (객체)

String s5 = new String("Hello"); // String object (객체)

 

s1, s2, s3의 경우 Hello라는 공통의 값을 참조하기 때문에 메모리 효율성이 좋습니다. (공통 풀에 저장된 값을 참조)

반면 s4, s5와 같이 new로 생성한 경우 Heap 영역에 따로 따로 생성되기 때문에 메모리 관리에 비효율적입니다.

 

만약 String이 계속 수정 되어야 한다면 StringBuffer나 StringBuilder를 사용하세요

 

StringBuffer와 StringBuilder의 결과는 동일하지만 상황에 따라 쓰이는게 약간 다릅니다.

StringBuffer의 경우는 멀티스레드의 환경에서 안전하게 돌아가도록 만든 것이고

StringBuilder의 경우 싱글스레드의 환경에서 안전하게 돌아가도록 만든 것입니다.

 

 

결과적으로 String 객체들의 연산이 이루어지면, 새로운 객체를 계속 만들어내기 때문에 메모리 관리 측면에서 상당히 비효율적이다.
이러한 이유로 만들어진 메모리영역이 Heap 안에 있는 String Constant Pool이다. 

여기에는 기존에 만들어진 문자열 값이 저장되어 있고, s1과 s2처럼 리터럴로 생성된 같은 값을 가지는 객체는 같은 레퍼런스를 가지게 됩니다.

 

출처: https://ict-nroo.tistory.com/18 [개발자의 기록습관:티스토리]

 

메모리 주소 값 확인하는 법

<dependency> 
    <groupId>org.openjdk.jol</groupId> 
    <artifactId>jol-core</artifactId>    
    <version>0.10</version> 
</dependency>
System.out.println("Memory address: " + VM.current().addressOf(obj));

HashCode의 값과 addressOf의 값은 다르다

(글을 찾아보면 HashCode값이 주소값이라고 하지만 정확히 상기 라이브러리를 사용해 addressOf 메소드를 사용할 시 정확하게 나온다.)

 

String hashCodeStr1 = "abcde";
String hashCodeStr2 = new String("abcde");

long hashCode1 = hashCodeStr1.hashCode(); 
long hashCode2 = hashCodeStr2.hashCode(); 

System.out.println("hashCode1 : " + hashCode1); // hashCode1 : 92599395
System.out.println("hashCode2 : " + hashCode2); // hashCode2 : 92599395	
System.out.println(hashCodeStr1 ==  hashCodeStr2 ? "hashCodeStr1 == hashCodeStr2" : "hashCodeStr1 != hashCodeStr2");
// hashCodeStr1 != hashCodeStr2

hashCode의 값은 같지만 == 로 비교한 결과 다르다고 나온다.

 

참고로 == 의 경우 주소값을 비교하고 equals의 경우 해당 객체의 값을 비교합니다.

 

참고로 com.foo.Person@2f92e0f4 이렇게 객체값을 나오는 경우의 아래처럼 해석하시면 됩니다.

com.foo.MyType - 클래스의 이름. 즉, com.foo. 패키지의 MyType 클래스

@ - 문자열 구분자

2f92e0f4 - 객체의 hashcode

 

hashCode에 대한 참고 사이트 : https://brunch.co.kr/@mystoryg/132

hashCode에 대한 참고 사이트 : https://brunch.co.kr/@mystoryg/133

 

 

 

 

 

메모리릭이란

ld 영역에 계속 누적된 객체로 인해 Major GC가 빈번하게 발생하게 되면서,
프로그램 응답속도가 늦어지면서 성능 저하를 불러온다. 이는 결국 OutOfMemory Error로 프로그램이 종료
쓴 객체에 대한 참조를 해제하지 않으면 가비지 컬렉션의 대상이 되지 않아 계속 메모리가 할당 되는 메모리 누수 현상이 발생

 

 

메모리 누수 팁

일단 정확히 어디에서 메모리가 누수되는지는 각자가 코딩한 코드내용에 따라 다 다르기 때문에 한정짓기는 힘들지만 개발할 때 지양해야할 사항 및 지향해야할 사항 및 알아두면 좋은 내용으로 구성했습니다.

 

정확한 원인 파악은 메모리 덤프 후 메모리 덤프를 분석해야 합니다.

 

기본적으로 Stack에서 Heap에 있는 객체를 참조하고 있는 동안에는 해당 객체는 GC에 의해 소멸되지 않습니다. 그에 대한 해결 방안입니다.

 

1. 불변의 값인 경우 String을 선언하고 값이 변동하는 경우 StringBuilder나 StringBuffer를 사용한다.

2. Http 객체 및 DB Connection 등... 사용 후 close()를 잘 해준다.

3. 모두 공유하는 값이 있고 불변의 경우 static final로 static에 선언해준다.

4. 다 쓴 객체에 Null을 할당한다.

 

참고로 XML과 JSON 파싱은 메모리를 가장 많이 사용합니다.

 

제가 봤을 때 가장 중요한건 4번입니다.

웬만한 객체들은 JVM에서 GC를 해주기 때문에 문제가 없지만 메소드를 호출하며 객체 잔뜩 만들고 그 이후에 사용 안 되는 경우 및 엄청난  사용자가 이용할 경우 복합적인 이유로 인해 참조가 남아서 GC 대상에서 제외되어서 OOM(Out of Memory)가 나게 됩니다

물론 코딩하면서 메모리 구조가 어떻게 만들어지고 다 머리에 그려지고 GC타이밍 등 다 파악 해 코딩하는 경우 필요한 부분만 null 사용하면 됩니다.

 

 

 

참고로 2번도 close() 메소드 내용 까보면 null을 할당해주는 짓을 하고 있습니다.

private final static int ALLOC_SIZE = (int) (Runtime.getRuntime().maxMemory() * 0.60);

public void allocate() {

    System.out.println("Before first allocation");
    byte[] b = new byte[ALLOC_SIZE];
    System.out.println("After first allocation");

    System.out.println("Before second allocation");
    byte[] b2 = new byte[ALLOC_SIZE];
    System.out.println("After second allocation");

}

public static void main(String[] args) {
    new GCTest().allocate();
}

Null 처리를 안 한 경우

 

재할당이 안 됩니다.

 

private final static int ALLOC_SIZE = (int) (Runtime.getRuntime().maxMemory() * 0.60);

public void allocate() {

    System.out.println("Before first allocation");
    byte[] b = new byte[ALLOC_SIZE];
    System.out.println("After first allocation");
    b = null;
    System.out.println("After assign null to b");
    System.out.println("Before second allocation");
    byte[] b2 = new byte[ALLOC_SIZE];
    System.out.println("After second allocation");
}

public static void main(String[] args) {
    new allocate();
}

Null 처리를 한 경우

 

재할당이 가능해집니다.

 

 

참고로 배열의 경우 모든 인덱스의 null을 넣어줘야 합니다.

 

이렇게 함으로써 얻을 수 있는 부가적인 장점은 누군가가 악의적으로 혹은 실수로 해당 객체를 다시 사용하려 했을 때 NullPointException이 발생하며 사전에 에러를 발생시킬 수 있다는 점이 있습니다만 단점으로는 코드가 지저분하게 됩니다.

 

이러한 방법을 깔끔하게 해결할 수 있는 객체들이 나오기 시작했는데요 WeakHashMap 등이 있습니다.

 

 

출처 : https://js2prince.tistory.com/entry/Java-%EA%B0%9D%EC%B2%B4-%EC%82%AC%EC%9A%A9%ED%9B%84-null-%ED%95%A0%EB%8B%B9-%ED%95%B4%EC%95%BC%ED%95%98%EB%82%98-%EB%A7%90%EC%95%84%EC%95%BC-%ED%95%98%EB%82%98

 

 

GC 구조

 

GC 일반적 동작순서

 

 

Mark And Sweep Sometimes Compact
Mark - 참조 객체 파악
Sweep - 참조 안 하는 객체 삭제
Compact - Sweep하면서 비어있는 메모리의 단편화를 막기 위해 응집시키는 역할 (때때로 발생)

 

 

WAS(톰캣)을 사용해 구동시킬 때 메모리 크기를 정할 수 있다.

메모리크기가 큰 경우
  1. GC 발생횟수는줄어든다.
  2. GC 수행시간은길어진다.

메모리크기가 작은 경우
  1. GC 수행시간은적어진다.
  2. GC 발생횟수는증가한다.

 

메모리 크기에 따라 일장일단이 있어 잘 설정해야합니다.

크기를 무제한으로 두는 경우 서버의 메모리를 다 차지해 서버가 뻗는 경우도 존재하지만 이미 WAS가 뻗어버리면

서비스가 안 되므로 그거나 그거나 인 거 같습니다.

 

GC 종류

 

Minor GC

Young 영역에서 발생하는 GC

 

Major GC

Old 영역에서 발생하는 GC

 

Full GC

Young과 Old영역에 생성된 객체를 모두 지웁니다.

Full GC는 속도가 매우 느리고, Full GC가 발생하는 순간, 자바 어플리케이션이 멈춥니다. 
따라서 Full GC는 성능과 안정성에 아주 큰 영향을 미칩니다.

Old 영역으로 이동하는 객체의 수를 줄이면 Full GC가 발생하는 빈도를 많이 줄일 수 있습니다.
최대한 FULL GC는 나오면 안 되며 웬만해서 Minor GC에서 다 처리하도록 해야합니다.

그렇다고 Full GC 실행 시간을 줄이기 위해서 Old 영역의 크기를 줄이면 자칫 OutOfMemoryError가 발생하거나 Full GC 횟수가 늘어난다. 
반대로 Old 영역의 크기를 늘리면 Full GC 횟수는 줄어들지만 실행 시간이 늘어난다. Old 영역의 크기를 적절하게 잘 설정해야 한다.

FULL GC에는 다양한 FULL GC가 존재한다. (Parallel GC Concurrent GC Train GC....등)

 

 

JAVA7까지 PERM 영역이 존재하는데 이는 영구적일 것이라고 생각하는 객체들을 관리합니다.

이 영역은 GC 대상에서 제외됩니다. 하지만 이 때문에 GC가 안 되어 OOM이 발생해 뻗어버리는 경우가 발생합니다.

 

최근 Java 8에서는 JVM 메모리 구조적인 개선 사항으로 Perm 영역이 Metaspace 영역으로 전환되고 기존 Perm 영역은 사라지게 되었다.  
Metaspace 영역은 Heap이 아닌 Native 메모리 영역(서버 메모리)으로 취급하게 된다. 

(Heap 영역은 JVM에 의해 관리된 영역이며, Native 메모리는 OS 레벨에서 관리하는 영역으로 구분된다) 

Metaspace가 Native 메모리를 이용함으로서 개발자는 영역 확보의 상한을 크게 의식할 필요가 없어지게 되었다.)
즉 개발자는 이 영역에 대해서 신경쓰지 않아도 JVM이 알아서 늘려주는 역할을 해줍니다.

 

 

출처 : https://www.youtube.com/watch?v=Fe3TVCEJhzo&t=260s

반응형