Title: 아이템 83. 지연 초기화는 신중히 사용하라 · Study-2-Effective-Java · Discussion #188 · GitHub
Open Graph Title: 아이템 83. 지연 초기화는 신중히 사용하라 · Study-2-Effective-Java · Discussion #188
X Title: 아이템 83. 지연 초기화는 신중히 사용하라 · Study-2-Effective-Java · Discussion #188
Description: 아이템 83. 지연 초기화는 신중히 사용하라
Open Graph Description: 지연 초기화(lazy initialization)란? 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법 주 용도는 크게 1) 최적화, 2) 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제 해결 용도로 사용 지연 초기화 == ⚔️ 최선의 조언 "지연 초기화는 필요할 때까지는 하지 말라" feat. #167 클래스 or 인스턴스 생성 ...
X Description: 지연 초기화(lazy initialization)란? 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법 주 용도는 크게 1) 최적화, 2) 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제 해결 용도로 사용 지연 초기화 == ⚔️ 최선의 조언 "지연 초기화는 필요할 때까지는 하지 말라" feat. #167 클래스 o...
Opengraph URL: https://github.com/orgs/Study-2-Effective-Java/discussions/188
X: @github
Domain: patch-diff.githubusercontent.com
{"@context":"https://schema.org","@type":"QAPage","mainEntity":{"@type":"Question","name":"아이템 83. 지연 초기화는 신중히 사용하라","text":"지연 초기화(lazy initialization)란?
\n\n필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법
\n주 용도는 크게 1) 최적화, 2) 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제 해결 용도로 사용
\n
\n지연 초기화 == ⚔️
\n최선의 조언
\n\n\"지연 초기화는 필요할 때까지는 하지 말라\"
\n
\n\n- feat. #167
\n- 클래스 or 인스턴스 생성 시 초기화 비용은 줄지만, 지연 초기화하는 필드 접근 비용은 커짐
\n
\n성능을 느리게 할 수도 있는 요인
\n\n- 지연 초기화 대상 필드 중 초기화가 이뤄지는 비율에 따라
\n- 실제 초기화에 드는 비용에 따라
\n- 초기화된 각 필드를 얼마나 빈번히 호출하느냐에 따라
\n
\n그러면 언제 지연 초기화를 쓰라는 거죠?
\n\n- 해당 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율은 낮고, 그 필드를 초기화하는 비용이 클 경우
\n- 유감스럽게도 이게 효용성이 있는지는 지연 초기화 적용 전후의 성능을 직접 측정해보는 방법밖에 없음
\n- 보통 일반적인 초기화가 지연 초기화보다 나음
\n
\n예시
\n일반적인 초기화 및 인스턴스 필드의 지연 초기화
\nNonmember.java
\npublic class Nonmember {\n\n private final String name;\n\n public Nonmember(String name) {\n this.name = name;\n }\n\n}
\nMember.java
\npublic class Member {\n\n private final String name;\n\n public Member(String name) {\n this.name = name;\n }\n\n}
\nDriven code
\npublic class MemberShell {\n\n private final Nonmember nonmember = this.createNonmember();\n private Member member;\n\n private Nonmember createNonmember() {\n return new Nonmember(\"Anonymous\");\n }\n\n /**\n * 지연 초기화가 초기화 순환성(initialization circularity)을 해소하기 위해서는 `synchronized` 접근자 메서드 사용\n */\n public /* synchronized */ Member getMember() {\n if (Objects.isNull(member)) {\n member = this.createMember();\n }\n\n return member;\n }\n\n private Member createMember() {\n return new Member(\"corock\");\n }\n\n}
\n정적 필드용 지연 초기화
\n\n- 성능 때문에 정적 필드를 지연 초기화해야 한다면 지연 초기화 홀더 클래스(lazy initialization holder class) 관용구 사용
\n
\npublic class MemberHolder {\n\n static final Member member = createMember();\n\n private static Member createMember() {\n return new Member(\"CoRock\");\n }\n\n public static Member getMember() {\n return MemberHolder.member;\n }\n\n}
\n\ngetMember() 메서드가 처음 호출되는 순간 MemberHolder.member 가 처음 읽히면서 MemberHolder 클래스 초기화 촉발 \n- 이 관용구는
getMember() 메서드가 필드에 접근하면서 동기화를 전혀 하지 않으므로 성능이 느려질 거리가 없어 유용함 \n
\n이중검사(double-check) 관용구
\n\n- 성능 때문에 인스턴스 필드를 지연 초기화해야 할 때 사용
\n- 초기화된 필드에 접근할 때 동기화 비용을 없애줌 (feat. #191)
\n- 한 번은 동기화 없이 검사하고, 두 번째는 동기화하여 검사하는 방식으로 필드의 값을 총 두 번 검사
\n- 필드가 초기화된 후로는 동기화하지 않으므로 해당 필드는 반드시
volatile 로 선언해야 함
\n(feat. #182) \n
\npublic class BlueCheck {\n\n private volatile Member member;\n\n private static Member createMember() {\n return new Member(\"CoRock\");\n }\n\n private Member getMember() {\n Member result = member;\n\n // 첫 번째 검사 (락 사용 안 함)\n if (Objects.nonNull(result)) {\n return result;\n }\n\n synchronized(this) {\n // 두 번째 검사 (락 사용)\n if (Objects.isNull(field)) {\n member = createMember();\n }\n }\n\n return MemberHolder.member;\n }\n\n}
\n\nresult 변수는 필드가 이미 초기화된 상황에서는 그 필드를 딱 한 번만 읽도록 보장하는 역할 \n- 반드시 필요하지는 않지만 성능을 높여주고, 저수준 동시성 프로그래밍에 표준적으로 적용되는 우아한 방법 (약 1.4배 성능 향상)
\n- 정적 필드에 이중검사도 적용할 수 있지만 그것보다 지연 초기화 홀더 클래스 방식이 더 나음
\n- 이따금 반복해서 초기화해도 상관없는 인스턴스 필드를 지연 초기화해야 할 때는 이중검사에서 두 번째 검사를 생략할 수 있음
\n- 모든 스레드가 필드의 값을 다시 계산해도 상관없고 필드 타입이
long 과 double 을 제외한 다른 기본 타입일 때, volatile 제거 가능\n\n- 이 변종을 짜릿한 단일검사(racy single-check) 관용구라고 함
\n- 이 관용구는 어떤 환경에서는 필드 접근 속도를 높여주지만, 초기화가 스레드당 최대 한 번 더 이뤄질 수 있음
\n- 하지만 이는 이례적인 기법이라 잘 사용하지 않는 편
\n
\n \n
\ncf. volatile
\n\n자바 변수를 CPU 캐시가 아닌 Main Memory 에 저장한다고 명시하는 것
\n변수 값을 Read & Write 시 Main Memory 로 접근
\n
\n\n\n\n
\n
\n \n\n\n\n
\n
\n \n\n
\n회고
\n\n- 무조건 지연 초기화로 하여 필요할 때만 불러오는 것이 좋은 방법으로만 알고 있었는데, 통념을 깨는 계기가 되었음
\n
\nReferences
\n","upvoteCount":1,"answerCount":1,"acceptedAnswer":{"@type":"Answer","text":"한 번은 동기화 없이 검사하고, 두 번째는 동기화하여 검사하는 방식으로 필드의 값을 총 두 번 검사
\n-> 이중 검사가 왜 ?? 성능을 높여주지 궁금하여 chatgpt에 물어봤습니다.
\n지연 초기화에서 한 번은 동기화 없이 필드를 검사하고, 두 번째는 동기화하여 검사하는 방식으로 필드의 값을 총 두 번 검사하는 이유는 성능 향상과 관련이 있습니다.\n\n첫 번째 검사에서는 필드의 값이 이미 초기화되어 있는지 확인합니다. 이 경우에는 두 번째 검사와 동기화를 할 필요가 없기 때문에 불필요한 동기화를 줄일 수 있습니다. 따라서 이로 인해 불필요한 오버헤드가 감소하고, 성능 향상이 이루어집니다.\n\n두 번째 검사에서는 필드의 값이 초기화되어 있지 않은 경우에만 동기화를 하여 필드를 초기화합니다. 이때, 동기화를 통해 다른 스레드가 필드의 값을 수정하는 것을 방지합니다. 또한, 이렇게 초기화된 필드는 이후에는 더 이상 동기화가 필요하지 않기 때문에 불필요한 오버헤드를 줄일 수 있습니다.\n\n이처럼 지연 초기화에서는 필드의 값을 필요한 시점에 초기화하는 방식으로 성능 향상을 이루고 있으며, 이를 위해 불필요한 동기화를 줄이는 것이 중요합니다.\n
","upvoteCount":1,"url":"https://github.com/orgs/Study-2-Effective-Java/discussions/188#discussioncomment-5520809"}}}
| route-pattern | /_view_fragments/Voltron::DiscussionsFragmentsController/show/orgs/:org/:discussion_number/discussion_layout(.:format) |
| route-controller | voltron_discussions_fragments |
| route-action | discussion_layout |
| fetch-nonce | v2:39c1a654-1a27-2929-eba8-add345d086f8 |
| current-catalog-service-hash | 9f0abe34da433c9b6db74bffa2466494a717b579a96b30a5d252e5090baea7be |
| request-id | A6D2:B851A:D24EEA:1190E32:696EEA86 |
| html-safe-nonce | 2b9038a6d0fec623b8b20554ea6bed981f2f5ebe8db0570d481b2947adbdc46f |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBNkQyOkI4NTFBOkQyNEVFQToxMTkwRTMyOjY5NkVFQTg2IiwidmlzaXRvcl9pZCI6IjI2OTQ3MDM3NTM2MjAwMjM5NDIiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 643d481c41a160283c57c21a264f492f6c9d10de1667fd91d2d046c0002a77c8 |
| hovercard-subject-tag | discussion:5027608 |
| github-keyboard-shortcuts | repository,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/Voltron::DiscussionsFragmentsController/show/orgs/Study-2-Effective-Java/188/discussion_layout |
| twitter:image | https://opengraph.githubassets.com/10c18f3d766ed55986ab0b106a964eb3335f581b0581bd31c0f3de779311bdaf/orgs/Study-2-Effective-Java/discussions/188 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/10c18f3d766ed55986ab0b106a964eb3335f581b0581bd31c0f3de779311bdaf/orgs/Study-2-Effective-Java/discussions/188 |
| og:image:alt | 지연 초기화(lazy initialization)란? 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법 주 용도는 크게 1) 최적화, 2) 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제 해결 용도로 사용 지연 초기화 == ⚔️ 최선의 조언 "지연 초기화는 필요할 때까지는 하지 말라" feat. #167 클래스 or 인스턴스 생성 ... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | b278ad162d35332b6de714dfb005de04386c4d92df6475522bef910f491a35ee |
| turbo-cache-control | no-preview |
| octolytics-dimension-user_id | 120388640 |
| octolytics-dimension-user_login | Study-2-Effective-Java |
| octolytics-dimension-repository_id | 577325341 |
| octolytics-dimension-repository_nwo | Study-2-Effective-Java/Effective-Java |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 577325341 |
| octolytics-dimension-repository_network_root_nwo | Study-2-Effective-Java/Effective-Java |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 39aed5006635ab6f45e6b77d23e73b08a00272a3 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width