영역 바이너리 옵션 타입

마지막 업데이트: 2022년 6월 1일 | 0개 댓글
  • 네이버 블로그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 트위터 공유하기
  • 카카오스토리 공유하기
Native Code(=Unmanaged Code) : 컴파일하면 OS에서 해석가능한 기계어로 번역된 코드
Managed Code : OS가 바로 이해할 수 있는 바이너리가 아닌, 임시 코드(IL, Intermediate Language)로 변환시키는 것으로, IL 코드가 실행되기 위해 JIT 컴파일러가 OS용 코드로 변환. (JIT 컴파일러는 OS마다 다름)

Spark Internal Part 2. Spark의 메모리 관리(1)

Spark 사용 시 여러가지 문제를 겪을 수 있는데, 그 중 가장 많이 겪게 되는 부분이 메모리 관련(주로 GroupBy 계열 Transformation을 사용할 때 발생하는 Out Of Memory Exception)문제입니다.

Spark 공식 문서를 보면 메모리 구조나 옵션 등에 대해 상세히 나와 있지만, 사용하다보면 옵션을 잘 적용했다고 해도 종종 오류가 발생하는 경우가 있습니다. 모든 내용이 Spark 문서에 기재되어 있지 않을 뿐더러(물론 Databricks에서는 많은 강의를 통해 내부 구조에 대해 설명하고 있긴 합니다), 써드파티 라이브러리(Parquet 등)에서의 문제(아래에서 기술할 예정입니다)도 있기 때문입니다.

물론 이 글에서도 전체 내용을 다루지는 못하며(워낙 Spark 코드가 방대하고 현재도 변화하고 있기 때문에), 일부 내용의 경우 잘못 이해하고 작성했을 수도 있습니다.

만일 글 내용 중 틀렸거나 부족한부분이 있다면 피드백 부탁드립니다. 글을 쓰고 공개적인 장소에 공유하는 이유는 이 글을 읽는 다른 분들께 약간이나마 도움을 드릴 수 있고, 저도 피드백을 받아 더 성장할 수 있다고 생각하기 때문입니다.

그럼, 시작하겠습니다.(아래부터는 말투가 바뀌어도 이해해주시기 바랍니다)

Spark의 메모리 관리를 알아보기 전에, JVM Object Memory Layout, Garbage Collection, Java NIO, Netty Library 등에 대한 이해가 필요하다.

첫 두 용어(JVM Object Memory Layout, Garbage Collection)는 Spark의 메모리 구조나 관리가 왜 현재와 같이 설계되었는지에 대한 원인에 가까우며, 나머지 두 용어(Java NIO, Netty Library)의 경우 이러한 원인의 해결책에 가깝다고 할 수 있다.

물론 이러한 주제를 모른다고 해서 Spark의 메모리 구조를 파악하는데 큰 어려움이 있는 것은 아니며, 대부분의 Spark 사용자들은 많은 메모리 관련 오류를 겪으며 어렴풋이 메모리 구조에 대한 이해가 생겼을 수도 있다고 생각한다.

하지만 이 내용들을 이해하고 넘어간다면, 좀 더 확실히 오류의 원인을 파악할 수 있고 빠르게 문제를 해결해 나갈 수 있다고 믿는다.

JVM Object Memory Layout

From Java code to Java heap

This article gives you insight into the memory usage of Java code, covering the memory overhead of putting an int value…

이 내용은 IBM developerWorks의 From Java code to Java Heap을 참고하여 작성하였습니다.

32비트 OS에서 java 명령어를 사용하여 Java Application을 실행 시켰을 때, 점유하는 메모리 공간을 크게 2가지 영역으로 나누어 볼 수 있다.

  • Native Heap 영역(JVM 자체는 Native Heap 영역의 일부를 사용한다)
  • Java Heap 영역

우리가 ‘일반적인’ 자바 프로그램을 작성할 떄 사용하는 부분은 Java Heap 영역이다.

Java에는 원시 타입(Primitive Type | int, boolean 등 기본형)과 참조 타입(Reference Type | Integer, Boolean, 사용자 정의 클래스 등)의 2가지 타입을 제공한다.

기본적으로 참조 타입은 원시 타입에 비해 많은 메모리를 사용한다. 참조 타입은 값에 대한 메모리 공간에 객체메타정보에 대한 메모리 공간도 추가적으로 사용하기 때문이다.

위 글에서는 int와 Integer를 기준으로 비교가 이루어졌는데, Integer의 경우 int보다 약 4배 정도의 메모리 공간을 소모한다고 기술되어 있다.

JVM 벤더에 따라 다를 수 있겠지만 Object 메타데이터는 보통 3가지로 이루어져 있다고 한다.

  • Class 포인터: 객체가 자신이 속한 클래스를 가리키는 포인터. Integer 객체의 경우 java.lang.Integer 클래스를 가리키고 있을 것이다(설명은 나와 있지 않지만, ClassLoader에 의해 PermGen 영역에 로드된 Class 객체를 가리킬 것이라 생각한다)
  • Flags: 객체 상태에 대한 정보(객체의 hashcode나 이 객체가 Array인지 아닌지 등)
  • Lock: 동기화(Synchronization)에 사용되는 정보(Object 기반 Lock을 건다면 사용될 듯 하다)

위 그림을 보면 Integer의 실제 값을 저장하는 공간을 32bit(4byte)인데 메타데이터가 96bit(12byte)를 사용하고 있는 것을 알 수 있다.

Array 객체(여기서는 primitive를 Element로 가진 Array로 설명했다)의 경우 Array Length를 알리는 Size 필드가 추가된다.

String과 같은 객체의 경우 내부적으로 Array[char]를 사용하기 때문에 위와 같이 2개의 메모리 구조를 사용하게 된다.

JVM 메모리 구조를 다루는 글은 아니기 때문에(후에 좀 더 깊이 공부하고싶은 내용이긴 하다) 이 정도까지의 내용을 기술하도록 하겠다.

여기까지 읽으신 분께서는 ‘생각보다 참조 타입이 메모리를 많이 사용하는구나’ 라고 생각하셨을 것이다. 더 나아가, ‘Spark과 같은 대용량 데이터 처리 프레임워크에서는 이러한 JVM Object의 Overhead를 줄이는 방향을 연구하겠구나’ 라는 생각도 하셨을 것이라 생각한다.

Garbage Collection

사실 Java의 Garbage Collection은 너무나 많은 국내/외 문서나 블로그에 잘 정리되어 있어, 이 글에 작성하는 것보다 그 글들을 보는 것이 더 효율적일 것이다.(Naver D2의 Java Garbage Collection이 잘 정리되어 있다고 생각하여 공유드린다)

NAVER D2

Java Garbage Collection

이 내용은 Databricks에서 2015년 5월 28일에 작성한 Tuning Java Garbage Collection for Apache Spark Applications의 내용과 내 생각(은 이텔릭체로 표기하겠습니다)을 바탕으로 작성하였다.

Garbage Collection은 ‘weak generational hypothesis’에 기반하여 만들어졌으며, ‘weak generational hypothesis’의 내용은 아래와 같다.

  • Most of the objects become unused quickly
  • The ones that do not usually survive for a (very) long time

대부분의 객체는 생성된 후 얼마가지 않아 사용되지 않으며, 살아남는 객체의 경우 매우 시간을 살아남는다는 것이다.

위의 객체를 RDD에 대응하여 생각해본다면 어떨까?

RDD[T]에 대해 map(f)를 호출하여 RDD[T]를 만든다고 생각해보자. 특정 Executor에 RDD의 파티션들이 할당되고, Executor는 연산을 위해 메모리에 파티션 데이터를 로드하고, 이 데이터들을 우리가 넘긴 함수 f(T->U)에 적용하여 새로운 RDD의 파티션을 만들어 낼 것이다.

함수 f에서 T에 해당하는 이전 데이터들은 U로 변환되고 나면 더이상 참조되어지지 않을 것이다. T에 해당하는 데이터들은 Minor GC가 발생했을 때 Survive하지 못하고 없어질 것이다.(여기까지는 ‘stop the world’가 발생하지 않기 때문에 큰 성능 이슈는 발생하지 않는다)

만일 만들어진 RDD들에 대해 cache를 호출한다면 어떻게 될까?

Spark의 Default Storage Level은 MEMORY_ONLY이기 때문에 cache를 호출하고, 다른 RDD들을 만들어나갈 때마다 cache된 RDD들은 Eden -> Survive -> Old로 이동해나갈 것이다.

결과적으로 메모리 공간이 부족해지면 Full GC가 발생하고, 이에 우리의 Executor JVM은 ‘stop the world’에 직면하여 작업이 지연될 것이다.

JVM Object Memory Layout 특성에 따라 하나의 값을 표현하는데 많은 메모리가 소요되고, 이러한 메모리 소요에 의해 부족해진 공간을 확보하기 위해 Garbage Collection이 발생한다.

대용량 데이터를 처리하는 프로그램에서는 이러한 특징들이 처리 속도를 저하시켰을 것이고, Spark 개발진 또한 이러한 문제를 해결하기 위해 고민했을 것이다. 현재 Spark에 적용되어 있는 다양한 기술(Project Tungsten, Netty)들은 이 고민에 대한 답이라 생각할 수 있을 것이다.

지금부터는 Spark 개발진이 이러한 문제를 해결하기 위하여 적용한 Project Tungsten과 Netty에 대해 알아보려 한다.

Project Tungsten

Project Tungsten은 Spark의 실행 엔진 개선(CPU와 메모리 위주)을 위해 2014년에 실행되었던 Umbrella 영역 바이너리 옵션 타입 영역 바이너리 옵션 타입 Project의 코드네임이다.

위의 Project Tungsten 링크로 들어가보면, 7가지 주요 특징에 대해 서술해 놓았는데, 이 중 Software Level에 가까운 3개를 추려보자면 아래와 같다.(사실 Code Generation 또한 Hardware Level에 가깝다고 생각하지만 위 페이지를 들어가 나머지 3개를 보면 왜 Code Generation이 Software Level에 가깝다고 말한지 이해할 것이다)

  • Memory Management and Binary Processing: 메모리를 좀 더 명시적으로 관리하며 JVM 객체와 Garbage Collection의 Overhead를 없애는 것
  • Cache-aware computation: 메모리 계층 구조(L1, L2, Memory, Disk 순으로 느려지니까)를 활용하는 알고리즘과 데이터 구조 도입
  • Code generation: 모던 컴파일러와 CPU를 활용할 수 있는 코드 생성

위 내용을 좀 더 자세히 설명해놓은 자료는 2015년 4월 28일에 올라온

Project Tungsten: Bringing Apache Spark Closer to Bare Metal

Project Tungsten focuses on improving the efficiency of memory and CPU for Spark applications. Motivated by…

이며, 이를 통한 Spark SQL의 성능 개선은

Spark SQL: Another 16x Faster After Tungsten - Databricks

Apache Spark 2.1.0 boosted the performance of Apache Spark SQL due to Project Tungsten software improvements. Another…

첫번째로 링크한 페이지의 내용 중 1. Memory Management and Binary Processing 을 정리해보았다.

처음 내용의 대부분은 이 글의 위에서 기재한 JVM Object Memory Layout과 Garbage Collection에서 오는 성능 저하에 대한 내용을 다루고 있다.

여기서부터 글 내용에 나오는 부분에 대해서는 일반적인 글씨체, 제 생각이 추가된 부분은 이텔릭체로 작성하겠습니다.

Spark의 경우 데이터가 어떤 방식으로 구성(Job, Stage, Task)되어 연산되는지, 데이터의 Life Cycle은 어떻게 되는지에 대한 정보를 가지고 있기 때문에 JVM Garbage Collector보다 더 효율적으로 메모리를 관리할 수 있다.

범용적인 목적의 Application을 가정하고 만들어진 ‘weak generational hypothesis’이 Spark에는 잘 적용되지 않는 모델이라고 생각하는 것 같다.

JVM 객체와 Garbage Collection의 오버헤드를 없애기 위해 JVM Object가 아니라 Binary Data로 직접 연산할 수 있도록 Spark 연산자를 변환하는 메모리 관리자(현재 Spark Code에서 확인할 수 있는 Unified Memory Manager를 가리키는 것 같다)를 도입했다.

사실 이 부분이 가장 애매하게 해석했던 문장인 것 같습니다. 제가 코드를 확인했을 때는(제가 못보거나 잘못 봤을 수도 있겠지만) 연산자를 바꾸는 동작은 확인하지 못했었으니까요. 이 부분에 대해서는 추후 소스 코드 분석 글에서 더 자세히 다루도록 하겠습니다.

이 기능은 C 스타일의 메모리 접근(malloc, dealloc 등)을 허용하는 sun.misc.UnSafe 기반으로 구현되었습니다.

대부분의 Java Class들은 구글에 검색해보면 Oracle JavaDoc이 나오는데, sun.misc.UnSafe의 경우에는 문서가 나오지 않는다. OpenJDK7 JavaDoc을 Hosting하는 http://www.docjar.com/html/api/sun/misc/Unsafe.java.html 페이지에서 겨우 발견할 수 있었다. 클래스 주석을 보면 다음과 같이 적혀 있다.

Low-Level의 Unsafe한 기능을 수행하는 함수들을 모아놓은 클래스이며, 이 클래스와 클래스에 속한 모든 methods는 Public 접근자이지만 Trusted Code에서만 이 객체를 얻을 수 있는 제한이 있습니다.

그렇다면 JVM Object를 사용하는 것과 Unsafe를 사용하는 것이 얼마나 차이가 있을까? 직접 테스트를 수행해 보았다.

  1. JVM Object를 사용하는 버전(Java로 작성)

Total Memory: 128974848
Max Memory: 1908932608
Free Memory: 127611672

Elapsed: 30192
Total Memory: 1547698176
Max Memory: 1908932608
Free Memory: 547102752

2. Unsafe를 사용하는 버전(Scala로 작성, 왜인지 모르겠지만 Java 환경에서 sun.misc.Unsafe가 import되지 않음…)

Total Memory: 128974848
Max Memory: 1908932608
Free Memory: 127611672
Elapsed: 200
Total Memory: 128974848
Max Memory: 1908932608
Free Memory: 126929656

눈여겨 보아야 할 점은 2가지이다.

Unsafe를 사용할 경우 200ms, Java Object를 사용했을 경우 30192ms가 소요되었다.(맥북에어 2014 기준)

언어의 차이를 배제하고도 거의 15배 정도의 차이가 발생하는 것을 확인할 수 있다.

2. Memory 사용량의 변화

위 두 코드 모두 Xmx, Xms 등의 Heap 영역 설정을 주지 않고 실행하였으며, Java의 경우 힙 사용량이 거의 0MB에서 800MB 정도로 늘어난 것을 확인할 수 있고, Unsafe를 사용한 Scala의 경우 힙 공간에 변동이 거의 없는 것을 확인할 수 있다. 이는 Unsafe가 위의 JVM Object Memory Layout에서 언급했던 Native Heap 영역을 사용하기 때문이다. Native Heap 영역의 경우 Garbage Collection 의 영향을 받지 않기 때문에 ‘stop the world’ 또한 발생하지 않는다.

이렇게 sun.misc.Unsafe를 사용하게 되면 JVM 에서 연산하는 것보다 속도/메모리 관리 면에서 더 많은 이점을 가지게 된다.

다시 Spark의 Tungsten 글로 돌아와서, Spark에서 java.util.HashMap을 UnSafe를 이용한 Binary Map으로 변경하여 성능 최적화 한 부분을 보여준다.

Java HashMap(LinkedHashMap인 것으로 추정된다)을 도식화 해놓았는데, 사실 좀 더 자세히 그렸다면 HashMap 자체의 Object 메타데이터, 각 Entry의 Object 메타데이터, Key의 Object 메타데이터, Value의 Object 메타데이터 등이 포함되어 매우 많은 메모리를 소모하는 것을 예상할 수 있다.

이에 반해 Spark Binary Map의 경우 선형적 메모리 공간에 Key,Value를 연속적으로 할당하였으며(mmap과 비슷한…), Java Object가 아니기 때문에 메타데이터 공간도 절약되고 Garbage Collection의 영향도 안 받게 된다.

따라서 아래와 같은 그래프에서 극적인 성능 개선이 이루어지는 것이다.

개인적으로 위의 도식과 비교 방식이 좀 아쉽다고 생각한다. 일단 HashMap의 경우 Linked List기반의 Linked HashMap이고 Binary Map의 경우 Array에 더 가까운 모습이다. 물론 이를 제외하고라도 성능 차이가 극명한 것은 눈에 보이긴 하지만…

이 기능은 Spark 1.4 부터 적용되어 있고 Spark SQL의 엔진에서도 사용하는 것으로 알고 있다.

Spark의 경우 UnifiedMemoryManager를 통해 Heap, Off-Heap 메모리의 비율이나 사용량을 측정/조절한다.

여기까지는 좋은데, Third Party Library에서 사용하는 Off-Heap 공간이 문제다. Netty, Parquet 등의 Third Party Library에서도 성능 향상을 위해 Unsafe를 사용하고 있는데, 이러한 Library들에서 사용하는 메모리 공간에 대해서는 Spark에서 관여하지 않는 듯 하다.

가끔 Parquet 포맷의 파일을 사용할 때 “java.lang.OutOfMemoryError: Direct buffer memory”와 같은 오류 메시지를 볼 수 있는데, Parquet Library에서 허용하는 Direct Memory 이상의 메모리를 사용했기 때문에 발생하게 된다.(https://issues.apache.org/jira/browse/SPARK-4073)

여기까지 Spark Tungsten Project에 관련된 내용을 정리하였다. Part 1에 비해 약간은 코드가 적고 이론적인 설명이 많다.

원래 Spark의 BlockManager, UnifiedMemoryManager, NettyUtils, PooledByteBufAllocator에 대한 코드 분석과 Java NIO, Netty를 연계한 설명도 이 글에 포함할 계획이었다.

하지만 워낙 양이 많아 하나의 글로 풀어 내기에는 방대할 것 같아 여기까지의 내용을 1편으로 정리하고 이어지는 2편에서 다룰 예정이다.

Difference 명령

가능한 경우 두 파일, 두 폴더의 파일 또는 선반과 로컬 또는 서버 파일 간의 차이점을 비교하고 표시합니다.

필수 구성 요소

차이 명령을 사용하려면 지정한 모든 항목에 대한 읽기 권한이 허용으로 설정되어 있어야 합니다. 자세한 내용은 기본 TFVC 권한을 참조하세요.

매개 변수

Argument

설명

필수 요소. 비교할 항목을 지정합니다. 버전 또는 경로가 지정되지 않은 경우 *현재 작업 영역 버전 *을 가정합니다. 로컬 및 Team Foundation 버전 제어 서버 경로를 모두 허용합니다.

Team Foundation이 itemspecs를 구문 분석하여 범위 내에 있는 항목을 확인하는 방법에 대한 자세한 내용은 Team Foundation 버전 제어 명령 사용을 참조하세요.

차이 명령은 와일드카드 문자를 지원하지 않습니다.

이 매개 변수는 /shelveset 옵션과 결합할 수 없습니다.

선택 사항입니다. itemspec을 비교할 항목입니다. 두 번째 itemspec을 제공하지 않으면 항목의 최신 Team Foundation 버전 제어 서버 버전이 사용됩니다.

/type 옵션에 대한 값을 제공합니다. "binary" 또는 "text"와 코드 페이지 번호 또는 코드 페이지의 식별 이름을 지정할 수 있습니다.

/format 옵션과 함께 다음 형식 중 하나의 출력 형식을 지정하는 데 사용됩니다.

이러한 출력 형식은 이 항목의 설명 섹션에 설명되어 있습니다.

/version 옵션에 대해 사용자가 제공한 값입니다. Team Foundation이 버전 사양을 구문 분석하여 범위 내에 있는 항목을 확인하는 방법에 대한 자세한 내용은 Team Foundation 버전 제어 명령 사용을 참조하세요.

사용자 이름으로 선반의 소유자를 식별합니다. 이 매개 변수에 대한 값을 제공하지 않으면 현재 사용자가 가정합니다.

선반의 이름을 지정합니다. 다른 사용자가 각 선반을 소유하는 한 Team Foundation Server 실행되는 서버에서 동일한 이름으로 둘 이상의 선반을 만들 수 있습니다.

기본 선반 버전과 비교할 폴더 또는 파일의 이름을 선반 집합에 지정합니다.

/login 옵션에 값을 제공합니다. 사용자 이름 값을 DOMAIN UserName 또는 UserName 으로 지정할 수 있습니다 .

옵션

설명

/type

검색된 인코딩을 재정의하고 지정된 인코딩을 사용하여 파일을 차이점 관리 엔진에 표시합니다.

/version

비교할 파일 또는 폴더의 버전을 지정합니다. 기본적으로 Team Foundation은 versionpec을 제공하지 않는 경우 작업 영역 버전을 사용합니다.

/version 플래그를 사용하는 대신 각 파일 이름의 끝에 세미콜론 및 버전 지정자를 추가하여 버전을 지정할 수 있습니다.

/format

형식 인수로 지정된 출력 형식 을 지정합니다.

/ignorespace

비교된 파일 간의 공백 차이를 강조 표시하지 않습니다.

/ignoreeol

두 파일 또는 파일 버전에서 새 줄 문자 간의 차이를 무시합니다. /ignoreeol은/ignoreSpace와 다르게 작동합니다. /ignorespace 는 8개의 공백을 1과 동일하게 처리합니다. 그러나 /ignoreeol 옵션을 사용하고 파일 A에 변경되지 않은 텍스트 영역 사이에 두 개의 새 줄 문자가 있고 파일 B에 1개가 있는 경우 결과가 차이로 표시됩니다. 두 파일 모두 새 줄이 하나만 있지만 파일 A가 새 줄로 사용하고 \r\n 영역 바이너리 옵션 타입 파일 B가 사용하는 \n 경우 /ignoreEOL 옵션은 이를 차이로 무시합니다.

/ignorecase

비교된 파일 간의 문자 대/소문자 차이는 강조 표시하지 않습니다.

/recursive

현재 폴더와 모든 하위 폴더의 차이점을 비교합니다.

/options

diff에서 호출할 도구의 옵션 문자열을 지정합니다. 자세한 내용은 파일 형식을 차이 도구와 연결 하고 파일 형식을 병합 도구와 연결합니다.

/shelveset

선반의 기반이 되는 Team Foundation 버전 제어 서버 버전과 비교할 선반 집합을 지정합니다.

이 옵션은 itemspec 인수와 결합할 수 없습니다. 개별 선반 항목을 비교하기 위해 shelveset_itemspec 제공할 수 있습니다.

/noprompt

이 작업을 완료하는 동안 표시되지 않는 대화 상자를 표시하지 않습니다.

/configure

사용자 도구 구성 대화 상자를 호출합니다. 이 도구는 Visual Studio 사용자 인터페이스에서 사용할 수 있습니다. 자세한 내용은 파일 형식을 차이점 도구와 연결합니다.

/login

Team Foundation Server 사용하여 사용자를 인증할 사용자 이름과 암호를 지정합니다.

명령줄에서 tf diff 또는 tf 차이를 입력하여 이 명령을 실행할 수 있습니다.

차이 명령을 사용하여 비교하고 가능한 경우 다음 사이의 차이를 표시할 수 있습니다.

두 개의 서로 다른 파일 또는 동일한 파일의 두 버전.

폴더에 있는 항목 중 하나 이상입니다.

Team Foundation Server 선반에 있는 항목 중 하나, 일부 또는 모든 항목

차이 명령을 사용하여 버전이 지정된 파일과 버전이 아닌 파일을 모두 비교할 수 있습니다.

Team Foundation은 모든 파일을 형식별로 분류합니다. 두 파일의 인코딩이 같으면 텍스트 파일을 나란히, 한 줄씩 병합하고 비교할 수 있습니다. 인코딩이 동일하지 않은 두 파일을 비교하려는 경우 /type 옵션을 사용하여 파일에 대한 인코딩 속성을 일시적으로 마스크하거나 재정의할 수 있습니다. 이진 파일은 비교할 수 있지만 병합할 수 없습니다. 하나 이상의 이진 파일을 차이 명령에 전달하는 경우 Team Foundation은 비교 중인 항목과 차이점이 있는지 여부를 나타냅니다. Team Foundation이 서로 다른 형식의 파일을 구분하고 처리하는 방법에 대한 자세한 내용은 파일 형식 관리를 참조하세요.

두 개의 파일 이름을 지정하면 두 파일이 비교됩니다. /version 플래그를 사용하는 대신 각 파일 이름의 끝에 세미콜론 및 버전 지정자를 추가하여 버전을 지정할 수 있습니다.

하나의 itemspec 만 차이 명령에 전달하는 경우:

versionpec을 제공하지 않으면 항목의 현재 작업 영역 버전이 기본적으로 기본 작업 영역 버전과 비교됩니다. 예를 들어 tf difference header.h는 header.h 의 현재 버전을 header.h의 기반이 되는 버전과 비교합니다.

itemspec에 versionspec을 포함하는 경우(예: tf difference header.h; LBeta1, Team Foundation은 해당 버전을 디스크의 현재 작업 영역 버전과 비교합니다.

/version:C1~C4와 같은 버전 범위를 지정하면 범위의 두 끝점에 있는 파일 버전이 비교됩니다.

tf 명령줄 유틸리티를 찾는 방법에 대한 자세한 내용은 Team Foundation 버전 제어 명령 사용을 참조하세요.

출력 형식 형식

/format 옵션과 함께 사용되는 format 매개 변수는 다양한 출력 형식을 지정합니다. 사용 가능한 출력 유형은 다음과 같습니다.

시각적 개체

시각적 형식 유형은 외부 차이 애플리케이션을 엽니다. 기본적으로 diffmerge.exe 시작됩니다.

간단한

간략한 형식은 비교 중인 파일이 다른지 여부를 인쇄합니다.

컨텍스트

컨텍스트 형식은 파일의 차이점에 대한 컨텍스트 줄을 제공합니다. 이 형식은 UNIX 기반 diff -c 출력 형식에서 파생됩니다.

RCS

RCS 형식은 /format:unix와 비슷하지만 컨텍스트 줄은 제공되지 않습니다.

파일 끝에 누락된 줄 끝 표식에 대한 특수 전달이 제공되지 않습니다.

SS

SS는 Visual SourceSafe 기본 차이 출력 형식입니다. 자세한 내용은 Diff(명령줄)영역 바이너리 옵션 타입 를 참조하세요.

SS_SideBySide

SS_SideBySide Visual SourceSafe 기본 병렬 출력 형식입니다.

SS_Unix

SS_Unix /format:unix 출력 형식과 비슷하지만 /format:ss_unix 컨텍스트 선 및 /format:unix는 포함하지 않습니다.

통합형

통합 형식은 UNIX 기반 diff -u 출력 형식에서 파생됩니다. /format:context/format:unified 가 아닌 경우 차이점 문자열 간에 동일한 컨텍스트 선을 반복합니다.

통합 형식은 다음 차이 문자열까지의 거리가 컨텍스트 줄 수보다 큰 경우에만 새 통합 차이 문자열(@@ . @@)을 생성합니다.

Unix

이 출력 형식은 UNIX 기반 diff 명령 출력 형식에서 파생됩니다.

Unix 출력 형식은 다음과 같은 방식으로 생성됩니다.

다음 예제에서는 Team Foundation 버전 제어 서버에서 체크 아웃된 파일의 버전인 314.cs의 로컬 버전과 314.cs의 작업 영역 버전 간의 차이점을 표시합니다.

c:\projectstf> difference 314.cs

다음 예제에서는 src 폴더에서 변경된 모든 파일을 표시합니다. src의 하위 폴더에서 변경된 파일을 표시하지 않습니다.

다음 예제에서는 변경 집합 3과 1254.cs의 변경 집합 8 간의 차이점을 표시합니다.

다음 예제에서는 레이블 "release"에 속하는 314.cs 버전과 변경 집합 3200에 속하는 버전 간의 차이점을 표시합니다.

다음 예제에서는 Nadia라는 사용자가 진열대 PeerCodeReview8에 보류한 e271.cs 버전과 변경 내용을 기반으로 하는 버전인 기본 선반 버전 간의 차이점을 표시합니다. 또한 Nadia가 선반에 있을 때 e271.cs에 대해 보류 중인 변경 유형도 표시합니다.

[1주차] JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.

Dismiss GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together. Sign up Millions of developers and companies build, ship, and maintain their software on GitHub - the largest and most advanced development platform in the world.

1. JVM이란 무엇인가

일반 애플리케이션의 코드는 OS 만 거치고 하드웨어로 전달되는데, Java 애플리케이션은 JVM 을 한 번 더 거칠뿐 아니라 하드웨어에 맞게 완전히 컴파일 된 상태가 아니라 실행 시에 해석(interpret)되기 때문에 속도가 느리다는 단점이 있다.

그래서 다른 OS 에 실행시키기 위해서는 애플리케이션을 그 OS 에 맞게 변경해야 한다. 하지만 이처럼 JVM 을 사용하는 Java 애플리케이션은 JVM 하고만 상호작용을 하기 때문에 OS 가 달라지더라도 프로그램의 변경 없이 실행이 가능하다.

심볼릭 레퍼런스 : 기본 자료형(primitive data type)을 제외한 모든 타입(클래스와 인터페이스)을 명시적인 메모리 주소 기반의 레퍼런스가 아니라 심볼릭 레퍼런스 를 통해 참조한다.

기본 자료형을 명확하게 정의하여 플랫폼 독립성 보장 : C/C++등의 전통적인 언어는 플랫폼에 따라 int형의 크기가 변하지만 JVM은 기본 자료형을 명확하게 정의하여 호환성을 유지하고 플랫폼 독립성을 보장한다.

네트워크 바이트 오더(network byte order) : 자바 클래스 파일은 네트워크 바이트 오더를 사용한다. 인텔 x86 아키텍처가 사용하는 리틀 엔디안 이나, RISC계열 아키텍처가 주로 사용하는 빅 엔디안 사이에서 플랫폼 독립성을 유지하려면 고정된 바이트 오더를 유지해야 하기에 네트워크 전송 시에 사용하는 바이트 오더인 네트워크 바이트 오더를 사용한다. 네트워크 바이트 오더는 빅 엔디안 이다.

바이트 오더: 영역 바이너리 옵션 타입 시스템이 어떠한 데이터를 표현하는 방법을 말한다. 빅엔디안: 상위 바이트의 값이 메모리상에 먼저 표시되는 방법 리틀엔디안: 하위 바이트의 값이 메모리상에 먼저 표시되는 방법

2. 컴파일 하는 방법

JVM 컴파일 과정

Terminal에서 컴파일

3. 실행하는 방법

4. 바이트코드란 무엇인가

특정 하드웨어가 아닌 가상 컴퓨터(Virtual Machine)에서 돌아가는 실행 프로그램을 위한 이진 표현법으로 하드웨어가 아닌 소프트웨어에 의해 처리되기에 기계어보다 추상적이다.

자바 바이트코드(Java bytecode)

자바코드를 바이트코드로 변환

5. JIT 컴파일러란 무엇이며 어떻게 동작하는지

자바 바이트 코드는 기계가 바로 수행할 수 있는 언어보다는 비교적 인간이 보기 편한 형태로 기술된 것으로 실행 엔진은 이와 같은 바이트코드를 JVM내부에서 기계가 실행할 수 있는 형태로 변경하는데 이때 인터프리터 , JIT 컴파일러 를 사용한다.

JIT 컴파일러란

: JIT(Just-In-Time) 컴파일러는 인터프리터의 단점을 보완하기 위해 도입되었다. 인터프리터 방식으로 실행하다 적절한 시점에 바이트코드 전체를 네이티브 코드로 바꾼다. 그 다음부터 인터프리터는 네이티브 코드로 컴파일된 코드를 바로 사용한다. 네이티브 코드를 실행하는 것이 하나씩 인터프리팅 하는 것보다 빠르고, 네이티브 코드는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 계속 빠르게 수행된다.

JIT 컴파일러 가 컴파일하는 과정은 바이트코드를 영역 바이너리 옵션 타입 영역 바이너리 옵션 타입 하나씩 인터프리팅하는 것보다 훨씬 오래걸리기에 만약, 한 번만 실행되는 코드라면 컴파일 하지 않고 인터프리팅하는게 유리하다. 따라서 컴파일러를 사용하는 JVM들은 내부에서 해당 메소드의 수행빈도를 확인 후 일정 정도를 넘을 때 컴파일을 수행한다.

JIT 컴파일러 동작 형태

Intermediate Representation: 소스 코드를 표현하기 위해 컴파일러 또는 가상 시스템에서 내부적으로 사용하는 데이터 구조 또는 코드로 최적화와 번역 등 추가처리를 위해 도움이 되도록 설계되었다.

6. JVM 구성 요소

클래스 로더 시스템

위임 모델 : 계층 구조를 바탕으로 클래스 로더끼리 로드를 위임하는 구조로 동작한다. 클래스를 로드할 때 먼저 상위 클래스 로더를 확인하여 상위 클래스 로더에 있다면 해당 클래스를 사용하고 없다면 로드를 요청받은 클래스로더가 클래스를 로드한다.

언로드 불가: 클래스 로더는 클래스를 로드할 수는 있지만 언로드할 수는 없다. 언로드 대신, 현재 클래스 로더를 새로 삭제하고 아예 새로운 클래스 로더를 생성하는 방법을 사용할 수 있다.

컴파일러에 의해 생성 된 클래스 파일은 시스템 또는 OS와 독립적이기에 모든 시스템에서 실행이 가능합니다. 실행을 위해 기본 클래스 파일(main 메소드를 포함하는 클래스)이 JVM으로 전달된 다음 최종 코드가 실행되기 전 주요 3가지의 단계를 거칩니다.

PC Register : 쓰레드가 생성될 때마다 생성되는 영역으로 현재 쓰레드가 실행되는 부분의 주소와 명령을 저장하고 있는 영역이다. 이것을 이용해 쓰레드를 돌아가며 수행할 수 있게 한다.

Understanding Java Virtual Machine (JVM) run-time data areas are critical to better Java programming. One of the most dreaded errors in Java is OutOfMemoryError and it is related to Java Virtual Machine (JVM) memory areas. We should have better understanding of JVM internals, how its data area works so that we will have better grip [. ]

힙(heap) : new 키워드로 생성된 객체와 배열이 생성되는 영역이다. 메소드 영역에 로드된 클래스만 생성이 가능하고 Garbage Collector가 참조되지 않는 메모리를 확인/제거하는 영역이다.

실행 엔진(Execution Engine)

JNI(Java Native Interface) 네이티브 메소드 인터페이스 & 라이브러리

The Java Native Interface (JNI) is a framework that allows your Java code to call native applications and libraries written in languages such as C, C++ and Objective-C. To be honest, if you have any other choice besides using JNI, do that other thing.

JVM이란

JVM 구조

Class Loader

  • 사용자가 코드를 작성하면 위의 그림과 같이 Stock.java 파일이 작성
  • 이 .java 소스를 Java Compiler를 통해 Stock.class(바이트코드)로 생성
  • 이렇게 생성된 클래스 파일들을 운영체제로 부터 할당 받은 메모리 영역인 Runtime Data Area로 적재하는 역할 수행
  • 자바 어플리케이션이 실행중일 때 수행

개발자가 작성한 소스 코드를 바이너리 코드로 변환하는 과정 👉 목적 파일이 생성
이러한 작업을 해주는 프로그램 : 컴파일러(Compiler)

Execution Engine

  • Class 영역 바이너리 옵션 타입 Loader에 의해 메모리에 적재된 클래스(바이트코드)들을 기계어로 변경해 명령어 단위로 실행
    • 인터프리터(interpreter) 방식 : 명령어를 하나하나 실행
    • JIT(Just-In-Time) 컴파일러를 이용하는 방식 : 적절한 시간에 전체 바이트 코드를 네이티브 코드로 변경해 Execution Engine이 네이티브 코드를 실행하는 것으로, 성능을 높이는 방식.

    출처 : https://aboullaite.me/understanding-jit-compiler-just-in-time-compiler/

    Native Code(=Unmanaged Code) : 컴파일하면 OS에서 해석가능한 기계어로 번역된 코드
    Managed Code : OS가 바로 이해할 수 있는 바이너리가 아닌, 임시 코드(IL, Intermediate Language)로 변환시키는 것으로, IL 코드가 실행되기 위해 JIT 컴파일러가 OS용 코드로 변환. (JIT 컴파일러는 OS마다 다름)

    색 보정 영역

    레벨 환경에 색 보정 영역을 추가하고 볼륨을 사용하여 컬러 그레이딩을 적용하는 방법을 자세하게 살펴봅니다.

    Choose your operating system:

    색 보정 영역(Color Correct Regions) 을 사용하면 씬에서 환경 및 오브젝트의 색을 조정하고 보정할 수 있습니다. 예를 들어 인카메라 VFX 시나리오에서 현실 세계의 세트와 LED 월에 표시된 환경 간에 라이팅과 섀도를 일치시킬 수 있습니다.

    이미지 대체 텍스트

    환경에 구체 볼륨으로 자홍색을 적용하는 색 보정 영역이 있는 씬

    이 영역 바이너리 옵션 타입 페이지에 나와 있는 내용을 모두 살펴보고 나면 레벨에 색 보정 영역을 추가하고 세팅을 수정하는 방법을 이해할 수 있게 됩니다.

    씬에 색 보정 영역 을 추가하려면 다음 단계를 따릅니다.

    에디터 메인 메뉴의 편집(Edit) >플러그인(Plugins) 으로 이동하여 플러그인 창을 엽니다.

    이미지 대체 텍스트

    플러그인 창에서 기타(Other) 섹션에 있는 색 보정 영역 을 찾습니다. 활성화됨(Enabled) 체크박스를 체크하여 프로젝트에 색 보정 영역 을 추가합니다.

    이미지 대체 텍스트

    액터 배치(Place Actors) 패널에서 볼륨(Volumes) 카테고리를 선택합니다.


0 개 댓글

답장을 남겨주세요